├── README.md ├── day-01 └── hello.go ├── day-02 └── variables-functions.go ├── day-03 └── structs.go ├── day-04 └── stdlib.go ├── day-05 ├── packages.go └── util │ └── fact.go ├── day-06 └── server.go ├── day-07 └── server.go ├── day-08 ├── go.mod ├── go.sum └── main.go ├── day-09 └── main.go ├── day-10 ├── .gitignore ├── Makefile ├── go.mod ├── main.go └── pkg │ └── jpclient │ └── client.go ├── day-11 ├── .gitignore ├── Makefile ├── app │ └── server.go ├── go.mod ├── go.sum ├── main.go └── pkg │ ├── errors │ └── errors.go │ └── jsonplaceholder │ └── client.go ├── day-12 ├── go.mod ├── go.sum └── main.go └── samples ├── .gitignore ├── array.go ├── db ├── db-insert.go ├── db.go ├── go.mod ├── go.sum └── test.db ├── errors.go ├── log.go └── maps.go /README.md: -------------------------------------------------------------------------------- 1 | # learn-golang 2 | 3 | Learn Go in 10 days - a set of hands on tutorials. 4 | -------------------------------------------------------------------------------- /day-01/hello.go: -------------------------------------------------------------------------------- 1 | /* 2 | Day 1: Hello World 3 | 4 | https://golang.org/dl/ : download and install golang for your OS 5 | 6 | $ cd your/src/directory 7 | $ mkdir learning-golang 8 | $ cd learning-golang 9 | $ vi hello.go # copy and paste this code 10 | $ go run hello.go # run directly (doesn't create the compiled exec) 11 | */ 12 | 13 | package main 14 | 15 | import "fmt" 16 | 17 | func main() { 18 | fmt.Printf("hello, world\n") 19 | } 20 | 21 | /* 22 | NOTES: 23 | 24 | - Packages, import and main are kind of like in Java. We'll revisit later 25 | - "fmt" is a standard library package 26 | - main() should always be in a package named "main" 27 | 28 | */ 29 | -------------------------------------------------------------------------------- /day-02/variables-functions.go: -------------------------------------------------------------------------------- 1 | /* 2 | DAY 2: Variables and functions 3 | 4 | $ cd learn-golang 5 | $ mkdir day-02 6 | $ cd day-02 7 | $ vi variables-functions.go # copy this code here 8 | $ go run variables-functions.go 9 | */ 10 | 11 | package main 12 | 13 | import "fmt" 14 | 15 | func main() { 16 | var foo = 3 17 | bar := "Hello" 18 | greet(bar, foo) 19 | } 20 | 21 | func greet(greeting string, times int) { 22 | for i := 0; i < times; i++ { 23 | fmt.Println(greeting) 24 | } 25 | } 26 | 27 | /* 28 | EXPLANATION 29 | - Go is statically typed, but can infer type using the first assigned value (e.g. foo) 30 | - The x := y is a shorthand for var x = y. it declares the variable and assigns value 31 | - Notice that there are no ";" at the end of lines. they're added by the compiler 32 | - Go function arguments declarations start with name and end with type! 33 | - string and int are built in types 34 | - if and for statements are similar to C, Java and JavaScript, but the "()" are usually unnecessary 35 | */ 36 | -------------------------------------------------------------------------------- /day-03/structs.go: -------------------------------------------------------------------------------- 1 | /* 2 | DAY 3: Structs and methods on structs 3 | 4 | $ cd learn-golang 5 | $ mkdir day-03 6 | $ cd day-03 7 | $ vi stucts.go # copy this code here 8 | $ go run structs.go 9 | */ 10 | 11 | package main 12 | 13 | import "fmt" 14 | 15 | // define a struct 16 | type rect struct { 17 | width, height int 18 | name string 19 | } 20 | 21 | // define a method named perim() on rect type 22 | func (r *rect) perim() int { 23 | return 2*r.width + 2*r.height 24 | } 25 | 26 | // define another method 27 | func (r *rect) expand(i int) { 28 | r.width *= i 29 | r.height *= i 30 | } 31 | 32 | func main() { 33 | // use {} notation to instantiate struct 34 | var r = rect{ 35 | width: 20, 36 | height: 5, 37 | name: "bar", // <- yes, that comma is not a typo 38 | } 39 | 40 | fmt.Println("Perimiter:", r.perim()) 41 | r.expand(2) // since expand() receives a POINTER to the original rect "r", it modifies the original 42 | fmt.Println("Perimeter after expanding:", r.perim()) 43 | } 44 | 45 | /* 46 | EXPLANATION 47 | - Go has no classes or inheritance (don't panic, it's a good thing!) 48 | - If you want to logically group variables, you need to use structs 49 | - You can use above notation to define methods on structs 50 | - Note the named variable instead of the "this" keyword in such methods 51 | - By default, Go variables are always passed by value (i.e when you pass something 52 | between functions, you're passing copies) 53 | - If you want to reference the original object, you need to pass a pointer (*) 54 | - TRY: try removing the * from the expand method and see what happens!S 55 | */ 56 | -------------------------------------------------------------------------------- /day-04/stdlib.go: -------------------------------------------------------------------------------- 1 | /* 2 | DAY 4: Standard lib, multiple return values, defer 3 | 4 | $ cd learn-golang 5 | $ mkdir day-04 6 | $ cd day-04 7 | $ vi stdlib.go # copy this code here 8 | $ go run stdlib.go 9 | */ 10 | 11 | package main 12 | 13 | import ( 14 | "fmt" 15 | "io/ioutil" 16 | "net/http" 17 | ) 18 | 19 | func main() { 20 | var resp, err = http.Get("http://example.com/posts/1") 21 | if err != nil { 22 | fmt.Println("Unable to get from example.com") 23 | return 24 | } 25 | defer resp.Body.Close() 26 | 27 | body, err := ioutil.ReadAll(resp.Body) 28 | if err != nil { 29 | fmt.Println("Unable to read response from example.com") 30 | return 31 | } 32 | fmt.Println(string(body)) 33 | } 34 | 35 | /* 36 | EXPLANATION 37 | - line 13: Imports can be grouped together 38 | Go has a rich standard library -- you will rarely need frameworks 39 | The http package: // docs: golang.org/pkg/net/http/ 40 | - line 20: Go functions can return multiple values! 41 | Standard go error handling practices is: `return result, err` 42 | - line 25: The defer keyword tells go to execute the statement just before exiting the function 43 | Very nifty to do housekeeping/resource release type work 44 | */ 45 | -------------------------------------------------------------------------------- /day-05/packages.go: -------------------------------------------------------------------------------- 1 | /* 2 | DAY 5: Packages 3 | 4 | $ cd learn-golang 5 | $ mkdir day-05; cd day-05 6 | $ vi packages.go <- COPY packages.go here 7 | $ mkdir util; cd util 8 | $ vi fact.go <- COPY util/fact.go here 9 | $ cd .. 10 | $ go run packages.go 11 | */ 12 | 13 | package main 14 | 15 | import "./util" 16 | import "fmt" 17 | 18 | func main() { 19 | fact := util.Fact(5) 20 | fmt.Println(fact) 21 | } 22 | 23 | /* 24 | EXPLANATION 25 | - Think of a package as a collection of functions 26 | - A package named "util" must be in a directory named "util" 27 | - Package names are lowercase 28 | - Go has no public/private keyword, instead: 29 | - Identifiers that start with an upper case letter get exported (public)! 30 | - E.g. PublicFunction, privateFunction 31 | */ 32 | -------------------------------------------------------------------------------- /day-05/util/fact.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | // Fact returns the factorial of n 4 | func Fact(n int) int { 5 | if n == 0 { 6 | return 1 7 | } 8 | return n * Fact(n-1) 9 | } 10 | -------------------------------------------------------------------------------- /day-06/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | DAY 6: The day we write a simple REST API! 3 | 4 | $ cd learn-golang 5 | $ mkdir day-06; cd day-06 6 | $ vi server.go <- COPY code here 7 | $ go run server.go 8 | $ curl http://localhost:8080/hello?who=world # on separate terminal 9 | */ 10 | 11 | package main 12 | 13 | import ( 14 | "encoding/json" 15 | "fmt" 16 | "io" 17 | "log" 18 | "net/http" 19 | ) 20 | 21 | func main() { 22 | http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { 23 | type Greeting struct { 24 | Text string `json:"text"` 25 | Error string `json:"error,omitempty"` 26 | } 27 | 28 | who := r.URL.Query().Get("who") 29 | res := Greeting{} 30 | if who == "" { 31 | res.Error = "Greet who?" 32 | } else { 33 | res.Text = fmt.Sprintf("Hello %s", who) 34 | } 35 | 36 | jsonRes, _ := json.Marshal(res) 37 | io.WriteString(w, string(jsonRes)) 38 | }) 39 | 40 | err := http.ListenAndServe(":8080", nil) 41 | if err != nil { 42 | log.Println(err) 43 | } 44 | } 45 | 46 | /* 47 | EXPLANATION 48 | - line 22: 49 | Tell the http module to use a function to handle a route 50 | - line 23: 51 | Since Go is statically typed, we need structs to represent JSON 52 | - line 24: 53 | Text attribute needs to start with an upper case letter, otherwise it would be private 54 | So we need a separate annotation to tell go to "marshal" (encode) it as "text" in JSON 55 | - The rest you can learn from package docs 56 | */ 57 | -------------------------------------------------------------------------------- /day-07/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | DAY 7: Building 3 | 4 | $ cd learn-golang 5 | $ cp -r day-06 day-07 # we're building the code from the last day 6 | $ cd day-07 7 | $ go build # this creates an executable named day-07 8 | $ ./day-07 # run the server 9 | $ curl http://localhost:8080/hello?who=world # on separate terminal 10 | 11 | It's that simple! For more options: 12 | 13 | $ go help build 14 | */ 15 | 16 | package main 17 | 18 | import ( 19 | "encoding/json" 20 | "fmt" 21 | "io" 22 | "log" 23 | "net/http" 24 | ) 25 | 26 | func main() { 27 | http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { 28 | type Greeting struct { 29 | Text string `json:"text"` 30 | Error string `json:"error,omitempty"` 31 | } 32 | 33 | who := r.URL.Query().Get("who") 34 | res := Greeting{} 35 | if who == "" { 36 | res.Error = "Greet who?" 37 | } else { 38 | res.Text = fmt.Sprintf("Hello %s", who) 39 | } 40 | 41 | jsonRes, _ := json.Marshal(res) 42 | io.WriteString(w, string(jsonRes)) 43 | }) 44 | 45 | err := http.ListenAndServe(":8080", nil) 46 | if err != nil { 47 | log.Println(err) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /day-08/go.mod: -------------------------------------------------------------------------------- 1 | module day-08 2 | 3 | go 1.12 4 | 5 | require github.com/gin-gonic/gin v1.4.0 6 | -------------------------------------------------------------------------------- /day-08/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g= 3 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 4 | github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ= 5 | github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= 6 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 7 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 8 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 9 | github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= 10 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 11 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 12 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 13 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 14 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 15 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 16 | github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= 17 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 18 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 19 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 20 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 21 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 22 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 23 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 24 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= 25 | gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= 26 | gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= 27 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 28 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 29 | -------------------------------------------------------------------------------- /day-08/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | DAY 8: Using a third party package! 3 | 4 | $ mkdir day-08 5 | $ cd day-08 6 | $ go mod init day-08 7 | $ vi main.go # copy the code below 8 | $ go get 9 | $ go build 10 | $ ./day-08 11 | $ curl http://localhost:8080/hello/world 12 | */ 13 | 14 | package main 15 | 16 | import ( 17 | "net/http" 18 | 19 | "github.com/gin-gonic/gin" 20 | ) 21 | 22 | func main() { 23 | r := gin.Default() 24 | 25 | r.GET("/hello/:name", func(c *gin.Context) { 26 | c.JSON(http.StatusOK, gin.H{ 27 | "message": "Hello, " + c.Param("name"), 28 | }) 29 | }) 30 | 31 | r.Run() 32 | } 33 | 34 | /* 35 | EXPLANATION: 36 | - Go's equivalent of Express.js: https://github.com/gin-gonic/gin 37 | - Go's equivalent of npm init: go mod init 38 | - Go's equivalent of package.json: go.mod (created by go mod init) 39 | - Go's equivalent of npm install: go get (it reads your import statements directly) 40 | - Go's equivalent of package-lock.json: go.sum (created by go get) 41 | - When you run the sample, don't forget to checkout the server console output!! 42 | - The Gin API is pretty intuitive - check out the docs. 43 | */ 44 | -------------------------------------------------------------------------------- /day-09/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | DAY 9: Pointers (don't panic, they're easy) 3 | 4 | $ mkdir day-09 5 | $ cd day-09 6 | $ vi main.go # copy the code below 7 | $ go run main.go 8 | */ 9 | 10 | package main 11 | 12 | import "fmt" 13 | 14 | // Answer is just a struct used to demonstrate pointers 15 | type Answer struct { 16 | value int 17 | } 18 | 19 | func main() { 20 | answer := Answer{value: 42} 21 | answerAgain := answer 22 | answer.value = 43 23 | 24 | fmt.Println("Wihout pointers, things get passed by value") 25 | 26 | fmt.Println(answer) // output: {43} 27 | fmt.Println(answerAgain) // output: {42} (surprise, JS and Java folks!) 28 | 29 | fmt.Println("You want to pass by reference? Use pointers") 30 | 31 | pAnswer := &answer 32 | answer.value = 44 33 | fmt.Println(answer) // output: {44} 34 | fmt.Println(*pAnswer) // output: {44} (whew!) 35 | } 36 | 37 | /* 38 | EXPLANATION: 39 | - In Go, when you pass/assign a variable, you always get a COPY, never a reference 40 | - This is true for both primitive types and object types (i.e. structs) 41 | - Might be surprising to JS and JAVA devs, who expect objects to be passed/assigned by reference 42 | - In Go, if you want to pass/assign a struct without creating a copy, you have to use a POINTER 43 | - A pointer is just the memory address of another variable 44 | - "&"" is the pointer operator, and you can read "&p" as "address of p" 45 | - "*" is the deferencing operator, and you can read "*p" as "target of p" 46 | */ 47 | -------------------------------------------------------------------------------- /day-10/.gitignore: -------------------------------------------------------------------------------- 1 | bin -------------------------------------------------------------------------------- /day-10/Makefile: -------------------------------------------------------------------------------- 1 | # this is an old-school makefile -- that's all you need, really 2 | # to run `test`, just type in the command line: `make test` 3 | 4 | test: 5 | go run main.go 6 | 7 | build: 8 | go build -o bin/day-10 9 | 10 | clean: 11 | go clean -------------------------------------------------------------------------------- /day-10/go.mod: -------------------------------------------------------------------------------- 1 | module day-10 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /day-10/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "day-10/pkg/jpclient" 5 | "log" 6 | ) 7 | 8 | // a simple proxy server for: 9 | // http://jsonplaceholder.typicode.com/todos/{id} 10 | func main() { 11 | client := jpclient.NewClient() 12 | client.EnableMockMode(true) 13 | 14 | todo, err := client.FetchTodo("1") 15 | if err != nil { 16 | log.Println(err) 17 | return 18 | } 19 | 20 | log.Println(todo) 21 | } 22 | 23 | /* 24 | EXPLANATION: 25 | - line 5: 26 | If your go.mod file defines the current package as "day-10", 27 | and your package is inside a directory called pkg, the package 28 | can be imported as "day-10/pkg/". 29 | 30 | - line 10: 31 | The Go way to construct structs is using a package level 32 | constructor function, usually named packagename.NewObjectName(). 33 | DON'T use structs to group functions like you do in Java! 34 | Use packages to group functions. 35 | Use structs only to logically group variables. 36 | */ 37 | -------------------------------------------------------------------------------- /day-10/pkg/jpclient/client.go: -------------------------------------------------------------------------------- 1 | package jpclient 2 | 3 | // USAGE: 4 | // import "microservice/pkg/jpclient" 5 | // client := jsonplaceholder.NewClient() 6 | // todo, err := client.FetchTodo("1") 7 | // if err != nil { 8 | // log.Println(err) 9 | // } else { 10 | // log.Println(todo) 11 | // } 12 | 13 | import ( 14 | "errors" 15 | ) 16 | 17 | // Todo represents a todo object from the rest api 18 | type Todo struct { 19 | ID string `json:"id"` 20 | UserID string `json:"userId"` 21 | Title string `json:"title"` 22 | Completed bool `json:"completed"` 23 | } 24 | 25 | // Client is a client for http://jsonplaceholder.typicode.com/todos API 26 | type Client struct { 27 | isMocked bool // if true, returns mock results for test purposes 28 | baseURL string // currently hard coded 29 | } 30 | 31 | // NewClient creates a new Client 32 | func NewClient() *Client { 33 | return &Client{isMocked: false, baseURL: "http://jsonplaceholder.typicode.com"} 34 | } 35 | 36 | // EnableMockMode enables/disables mock mode 37 | func (c *Client) EnableMockMode(enable bool) { 38 | c.isMocked = enable 39 | } 40 | 41 | // FetchTodo fetches a Todo from /todos/{id} 42 | func (c *Client) FetchTodo(id string) (*Todo, error) { 43 | if c.isMocked { 44 | return &Todo{ID: "1", UserID: "john", Title: "Hello world", Completed: false}, nil 45 | } 46 | // TODO: implement 47 | return nil, errors.New("Not implemented") 48 | } 49 | 50 | /* 51 | EXPLANATIONS: 52 | - Everything above is just putting together what 53 | you have already learned. 54 | - But do notice how simple the Go way of writing 55 | doc comments are: // is 56 | */ 57 | -------------------------------------------------------------------------------- /day-11/.gitignore: -------------------------------------------------------------------------------- 1 | bin -------------------------------------------------------------------------------- /day-11/Makefile: -------------------------------------------------------------------------------- 1 | testrun: 2 | curl localhost:8080/todos/1 3 | 4 | build: 5 | go build -o bin/microservice 6 | 7 | clean: 8 | go clean 9 | 10 | run: 11 | ./bin/microservice -------------------------------------------------------------------------------- /day-11/app/server.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "microservice/pkg/jsonplaceholder" 5 | "net/http" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // Server wraps the gin server to create a proxy server for jsonplaceholder.typicode.com/todos API 11 | // using the jsonplaceholder client package in this project 12 | type Server struct { 13 | router *gin.Engine 14 | client *jsonplaceholder.Client 15 | } 16 | 17 | // NewServer creates a new Server instance 18 | func NewServer() *Server { 19 | client := jsonplaceholder.NewClient() 20 | client.EnableMockMode(false) 21 | 22 | s := &Server{} 23 | r := gin.Default() 24 | s.router = r 25 | s.client = client 26 | 27 | r.GET("/", func(c *gin.Context) { 28 | c.JSON(http.StatusOK, gin.H{ 29 | "version": "0.1", 30 | }) 31 | }) 32 | 33 | r.GET("/todos/:id", func(c *gin.Context) { 34 | todo, err := client.FetchTodo(c.Param("id")) 35 | if err != nil { 36 | c.JSON(http.StatusInternalServerError, gin.H{ 37 | "error": err.Error(), 38 | }) 39 | return 40 | } 41 | 42 | c.JSON(http.StatusOK, todo) 43 | }) 44 | 45 | return s 46 | } 47 | 48 | // Run creates the REST API 49 | func (s *Server) Run() error { 50 | return s.router.Run() 51 | } 52 | -------------------------------------------------------------------------------- /day-11/go.mod: -------------------------------------------------------------------------------- 1 | module microservice 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 // indirect 7 | github.com/gin-gonic/gin v1.3.0 8 | github.com/golang/protobuf v1.3.1 // indirect 9 | github.com/mattn/go-isatty v0.0.7 // indirect 10 | github.com/ugorji/go v1.1.4 // indirect 11 | gopkg.in/go-playground/validator.v8 v8.18.2 // indirect 12 | gopkg.in/yaml.v2 v2.2.2 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /day-11/go.sum: -------------------------------------------------------------------------------- 1 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g= 2 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 3 | github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs= 4 | github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= 5 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 6 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 7 | github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= 8 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 9 | github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= 10 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 11 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 13 | gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= 14 | gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= 15 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 16 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 17 | -------------------------------------------------------------------------------- /day-11/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "microservice/app" 6 | ) 7 | 8 | func main() { 9 | log.SetFlags(log.LstdFlags | log.Lshortfile) 10 | log.Println("Starting server") 11 | 12 | server := app.NewServer() 13 | err := server.Run() 14 | if err != nil { 15 | log.Printf(err.Error()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /day-11/pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | // import "microservice/pkg/errors" 4 | // USAGE: e := errors.New("Cannot reticulate splines").Code(123) 5 | 6 | import "fmt" 7 | 8 | // BasicError represents a basic error with an error code and a message 9 | type BasicError struct { 10 | code int 11 | message string 12 | } 13 | 14 | // Error is implemented by BasicError so that it can be used like the built in error type 15 | func (e *BasicError) Error() string { 16 | return fmt.Sprintf("[%d] %s", e.code, e.message) 17 | } 18 | 19 | // New creates a new BasicError 20 | func New(message string) *BasicError { 21 | return &BasicError{code: 0, message: message} 22 | } 23 | 24 | // Code sets the code, which will otherwise be 0 25 | func (e *BasicError) Code(code int) *BasicError { 26 | e.code = code 27 | return e 28 | } 29 | -------------------------------------------------------------------------------- /day-11/pkg/jsonplaceholder/client.go: -------------------------------------------------------------------------------- 1 | package jsonplaceholder 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "microservice/pkg/errors" 7 | "net/http" 8 | ) 9 | 10 | // Todo represents a todo object from the rest api 11 | type Todo struct { 12 | ID int `json:"id"` 13 | UserID int `json:"userId"` 14 | Title string `json:"title"` 15 | Completed bool `json:"completed"` 16 | } 17 | 18 | // Client is a client for jsonplaceholder.typicode.com REST API 19 | type Client struct { 20 | isMocked bool 21 | baseURL string 22 | client *http.Client 23 | } 24 | 25 | // NewClient creates a new Client 26 | func NewClient() *Client { 27 | return &Client{ 28 | isMocked: false, 29 | baseURL: "http://jsonplaceholder.typicode.com", 30 | client: &http.Client{}, 31 | } 32 | } 33 | 34 | // EnableMockMode enables/disables mock mode 35 | func (c *Client) EnableMockMode(enable bool) { 36 | c.isMocked = enable 37 | } 38 | 39 | // FetchTodo fetches a Todo from /todos/{id} 40 | func (c *Client) FetchTodo(id string) (*Todo, error) { 41 | if c.isMocked { 42 | return &Todo{ID: 1, UserID: 2, Title: "Hello world", Completed: false}, nil 43 | } 44 | 45 | resp, err := c.client.Get(c.baseURL + "/todos/" + id) 46 | if err != nil { 47 | return nil, errors.New("Could not reach server") 48 | } 49 | defer resp.Body.Close() 50 | 51 | body, err := ioutil.ReadAll(resp.Body) 52 | if err != nil { 53 | return nil, errors.New("Error reading server response") 54 | } 55 | 56 | if resp.StatusCode != http.StatusOK { 57 | return nil, errors.New("?").Code(400) 58 | } 59 | 60 | todo := &Todo{} 61 | err = json.Unmarshal(body, todo) 62 | if err != nil { 63 | return nil, errors.New("Server did not return valid Todo data") 64 | } 65 | 66 | return todo, nil 67 | } 68 | -------------------------------------------------------------------------------- /day-12/go.mod: -------------------------------------------------------------------------------- 1 | module day-12 2 | 3 | go 1.12 4 | 5 | require github.com/go-sql-driver/mysql v1.4.1 6 | -------------------------------------------------------------------------------- /day-12/go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 2 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 3 | -------------------------------------------------------------------------------- /day-12/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "log" 4 | import "database/sql" 5 | import _ "github.com/go-sql-driver/mysql" 6 | 7 | func main() { 8 | db, err := sql.Open("mysql", "root:hnl54321@tcp(127.0.0.1:3306)/test") 9 | if err != nil { 10 | log.Fatal(err) 11 | return 12 | } 13 | 14 | err = db.Ping() 15 | if err != nil { 16 | log.Println(err) 17 | return 18 | } 19 | log.Println("connected to db") 20 | 21 | var name = "" 22 | var owner = "" 23 | 24 | rows, err := db.Query("select name, owner from pet") 25 | if err != nil { 26 | log.Fatal(err) 27 | return 28 | } 29 | defer rows.Close() 30 | 31 | for rows.Next() { 32 | err = rows.Scan(&name, &owner) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | log.Println(name, owner) 37 | } 38 | 39 | defer db.Close() 40 | } -------------------------------------------------------------------------------- /samples/.gitignore: -------------------------------------------------------------------------------- 1 | log.txt -------------------------------------------------------------------------------- /samples/array.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | fmt.Println("arrays") 9 | primes := [5]int{1, 2, 3, 5} 10 | 11 | l := len(primes) 12 | 13 | for i := 0; i < l; i++ { 14 | fmt.Println(primes[i]) 15 | } 16 | 17 | for index, value := range primes { 18 | fmt.Printf("%d = %d\n", index, value) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/db/db-insert.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | 7 | _ "github.com/mattn/go-sqlite3" 8 | ) 9 | 10 | func main() { 11 | fmt.Println("Testing DB inserts...") 12 | 13 | db, err := sql.Open("sqlite3", "./test.db") 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | stmt, err := db.Prepare("insert into test(name, age) values (?, ?)") 19 | if err != nil { 20 | panic(err) 21 | } 22 | 23 | stmt.Exec("paul", 90) 24 | if err != nil { 25 | panic(err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /samples/db/db.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | 7 | _ "github.com/mattn/go-sqlite3" 8 | ) 9 | 10 | func main() { 11 | db, err := sql.Open("sqlite3", "./test.db") 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | rows, err := db.Query("select id, name, age from test") 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | for rows.Next() { 22 | var id int 23 | var name string 24 | var age int 25 | err := rows.Scan(&id, &name, &age) 26 | if err != nil { 27 | fmt.Println(err) 28 | } 29 | fmt.Printf("%d, %s, %d\n", id, name, age) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/db/go.mod: -------------------------------------------------------------------------------- 1 | module db 2 | 3 | go 1.12 4 | 5 | require github.com/mattn/go-sqlite3 v1.11.0 6 | -------------------------------------------------------------------------------- /samples/db/go.sum: -------------------------------------------------------------------------------- 1 | github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= 2 | github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 3 | -------------------------------------------------------------------------------- /samples/db/test.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hliyan/learn-golang/ab7080d74c4cf1c173736652614dd7d0e45e82e6/samples/db/test.db -------------------------------------------------------------------------------- /samples/errors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type myError struct { 6 | Code int 7 | Message string 8 | } 9 | 10 | func (e *myError) Error() string { 11 | return fmt.Sprintf("%d: %s", e.Code, e.Message) 12 | } 13 | 14 | func test() (int, error) { 15 | return 0, &myError{Code: 1, Message: "Not implemented"} 16 | } 17 | 18 | func main() { 19 | if _, err := test(); err != nil { 20 | fmt.Println(err) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /samples/log.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | file, err := os.Create("log.txt") 11 | if err != nil { 12 | fmt.Println("Error opening file: " + err.Error()) 13 | os.Exit(0) 14 | } 15 | defer file.Close() 16 | 17 | log.SetOutput(file) 18 | log.Println("Test") 19 | } 20 | -------------------------------------------------------------------------------- /samples/maps.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | func main() { 9 | ages := map[string]int{ 10 | "John": 30, 11 | "James": 31, 12 | } 13 | 14 | for key, value := range ages { 15 | fmt.Println(key + " = " + strconv.Itoa(value)) 16 | } 17 | 18 | ages["Bill"] = 40 19 | if billAge, billExists := ages["Bill"]; billExists { 20 | fmt.Println("Bill is in the map and he is " + strconv.Itoa(billAge) + " years old.") 21 | } 22 | } 23 | --------------------------------------------------------------------------------