├── .gitignore ├── LICENSE ├── README.md ├── binarypkg ├── binarypkg.zip ├── src │ └── github.com │ │ └── shijuvar │ │ └── binarypkg │ │ └── utils.go └── utils.go ├── binarypkgdemo └── main.go ├── ch01 ├── declarations │ ├── enum.go │ ├── main.go │ └── main1.go ├── favorites │ └── main.go ├── hello │ └── main.go ├── lib │ ├── favorites.go │ └── utils.go ├── loop │ └── main.go ├── strutils │ └── utils.go ├── strutilsdemo │ └── main.go ├── typeconv │ └── main.go └── vetting │ └── main.go ├── ch02 ├── arrays │ └── main.go ├── defer │ ├── deferfunc.go │ ├── panicrecover.go │ └── test.txt ├── functions │ ├── calc.go │ ├── closures.go │ ├── swap.go │ └── variadic.go ├── maps │ ├── main.go │ └── sort_map.go └── slices │ ├── append.go │ ├── append_nilslice.go │ ├── copy.go │ ├── iterate.go │ ├── main.go │ └── slicing.go ├── ch03 ├── ecommerce │ ├── main.go │ └── models.go ├── employee │ └── employee.go ├── person.go └── pointer │ └── main.go ├── ch04 ├── channels │ └── main.go ├── deadlock │ ├── main.go │ └── main_deadlock.go ├── mathtable │ └── main.go ├── pipeline │ ├── main.go │ └── main1.go ├── select │ ├── context.go │ └── main.go ├── unbuffercounter │ ├── main.go │ └── main1.go └── worker │ └── main.go ├── ch05 ├── archivetar │ ├── main.go │ ├── readme.txt │ └── source.tar ├── archivezip │ ├── main.go │ ├── readme.txt │ └── source.zip ├── cmdflags │ └── main.go ├── flag │ └── main.go ├── json │ └── main.go ├── jsontag │ └── main.go ├── log │ ├── logger.go │ ├── logs.txt │ └── main.go └── simplelog │ └── main.go ├── ch06 ├── influx │ └── main.go ├── mongo │ ├── bookmark_store.go │ └── main.go ├── postgres │ └── main.go └── rethink │ ├── bookmark_store.go │ └── main.go ├── ch07 ├── bookmarkapi │ ├── common │ │ ├── auth.go │ │ ├── bootstrapper.go │ │ ├── config.json │ │ ├── logger.go │ │ ├── mongo_utils.go │ │ └── utils.go │ ├── controllers │ │ ├── bookmark_controller.go │ │ ├── resources.go │ │ └── user_controller.go │ ├── keys │ │ ├── app.rsa │ │ └── app.rsa.pub │ ├── logs.txt │ ├── main.go │ ├── model │ │ └── models.go │ ├── routers │ │ ├── bookmark.go │ │ ├── routers.go │ │ └── user.go │ └── store │ │ ├── bookmark_store.go │ │ └── user_store.go ├── customhandler │ └── main.go ├── defaultservemux │ └── main.go ├── handlefunc │ └── main.go ├── handlerfunc │ └── main.go ├── httpserver │ └── main.go ├── middleware │ └── main.go └── server │ └── main.go ├── ch08 ├── calc │ ├── calc.go │ └── calc_test.go ├── httpbdd │ ├── controllers │ │ ├── controllers_suite_test.go │ │ ├── user_controller.go │ │ └── user_controller_test.go │ ├── main.go │ ├── model │ │ └── user.go │ └── store │ │ └── user_store.go └── httptest │ ├── main.go │ └── main_test.go └── grpc ├── client └── main.go ├── customer ├── customer.pb.go └── customer.proto ├── readme.txt └── server └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | logs.txt 23 | *.exe 24 | *.test 25 | *.prof 26 | *.txt 27 | debug 28 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Shiju Varghese 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go Recipes 2 | Code examples for the book "Go Recipes" by Apress. 3 | 4 | # gokit 5 | Check out [github.com/shijuvar/gokit](https://github.com/shijuvar/gokit) for further examples on Go. 6 | 7 | ## Articles 8 | * [Building High Performance APIs In Go Using gRPC And Protocol Buffers](https://medium.com/@shijuvar/building-high-performance-apis-in-go-using-grpc-and-protocol-buffers-2eda5b80771b) 9 | * [Using Binary-Only Packages In Go](https://medium.com/@shijuvar/using-binary-only-packages-in-go-667bd7b123c8) 10 | 11 | -------------------------------------------------------------------------------- /binarypkg/binarypkg.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shijuvar/go-recipes/1ae7566656200ddab6d66ad478e9feb810690005/binarypkg/binarypkg.zip -------------------------------------------------------------------------------- /binarypkg/src/github.com/shijuvar/binarypkg/utils.go: -------------------------------------------------------------------------------- 1 | //go:binary-only-package 2 | 3 | package binarypkg 4 | -------------------------------------------------------------------------------- /binarypkg/utils.go: -------------------------------------------------------------------------------- 1 | package binarypkg 2 | 3 | import "strings" 4 | 5 | // ToUpperCase returns the string changed with upper case. 6 | func ToUpperCase(s string) string { 7 | return strings.ToUpper(s) 8 | } 9 | 10 | // ToLowerCase returns the string changed with lower case. 11 | func ToLowerCase(s string) string { 12 | return strings.ToLower(s) 13 | } 14 | -------------------------------------------------------------------------------- /binarypkgdemo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/shijuvar/go-recipes/binarypkg" 7 | ) 8 | 9 | func main() { 10 | str := "Golang" 11 | // Convert to upper case 12 | fmt.Println("To Upper Case:", binarypkg.ToUpperCase(str)) 13 | 14 | // Convert to lower case 15 | fmt.Println("To Lower Case:", binarypkg.ToLowerCase(str)) 16 | 17 | } 18 | -------------------------------------------------------------------------------- /ch01/declarations/enum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | const ( 6 | // UNSPECIFIED logs nothing 7 | UNSPECIFIED Level = iota // 0 : 8 | // TRACE logs everything 9 | TRACE // 1 10 | // INFO logs Info, Warnings and Errors 11 | INFO // 2 12 | // WARNING logs Warning and Errors 13 | WARNING // 3 14 | // ERROR just logs Errors 15 | ERROR // 4 16 | ) 17 | 18 | // Level holds the log level. 19 | type Level int 20 | 21 | // levels provides the string name of Level 22 | var levels = [...]string{ 23 | "UNSPECIFIED", 24 | "TRACE", 25 | "INFO", 26 | "WARNING", 27 | "ERROR", 28 | } 29 | 30 | // String returns the string value of level 31 | func (l Level) String() string { 32 | return levels[l] 33 | } 34 | 35 | func main() { 36 | level := TRACE 37 | if level == TRACE { 38 | fmt.Println("TRACE") 39 | } 40 | level = INFO 41 | fmt.Println(level.String()) 42 | } 43 | -------------------------------------------------------------------------------- /ch01/declarations/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Declare constant 6 | const Title = "Person Details" 7 | 8 | // Declare package variable 9 | var Country = "USA" 10 | 11 | func main() { 12 | fname, lname := "Shiju", "Varghese" 13 | age := 35 14 | // Print constant variable 15 | fmt.Println(Title) 16 | // Print local variables 17 | fmt.Println("First Name:", fname) 18 | fmt.Println("Last Name:", lname) 19 | fmt.Println("Age:", age) 20 | // Print package variable 21 | fmt.Println("Country:", Country) 22 | } 23 | -------------------------------------------------------------------------------- /ch01/declarations/main1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Declare constant 6 | const Title string = "Person Details" 7 | 8 | // Declare package variable 9 | var Country string = "USA" 10 | 11 | func main() { 12 | var fname, lname string = "Shiju", "Varghese" 13 | var age int = 35 14 | // Print constant variable 15 | fmt.Println(Title) 16 | // Print local variables 17 | fmt.Println("First Name:", fname) 18 | fmt.Println("Last Name:", lname) 19 | fmt.Println("Age:", age) 20 | // Print package variable 21 | fmt.Println("Country:", Country) 22 | } 23 | -------------------------------------------------------------------------------- /ch01/favorites/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | fav "github.com/shijuvar/go-recipes/ch01/lib" 7 | ) 8 | 9 | func main() { 10 | // Print default favorite packages 11 | fmt.Println("****** Default favorite packages ******\n") 12 | fav.PrintFavorites() 13 | // Add couple of favorites 14 | fav.Add("github.com/dgrijalva/jwt-go") 15 | fav.Add("github.com/onsi/ginkgo") 16 | fmt.Println("\n****** All favorite packages ******\n") 17 | fav.PrintFavorites() 18 | count := len(fav.GetAll()) 19 | fmt.Printf("Total packages in the favorite list:%d", count) 20 | } 21 | -------------------------------------------------------------------------------- /ch01/hello/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("Hello, World") 7 | } 8 | -------------------------------------------------------------------------------- /ch01/lib/favorites.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | // Stores favorites 4 | var favorites []string 5 | 6 | // Initialization logic for the package 7 | func init() { 8 | favorites = make([]string, 3) 9 | favorites[0] = "github.com/gorilla/mux" 10 | favorites[1] = "github.com/codegangsta/negroni" 11 | favorites[2] = "gopkg.in/mgo.v2" 12 | } 13 | 14 | // Add a favorite into the in-memory collection 15 | func Add(favorite string) { 16 | favorites = append(favorites, favorite) 17 | } 18 | 19 | // Returns all favorite 20 | func GetAll() []string { 21 | return favorites 22 | } 23 | -------------------------------------------------------------------------------- /ch01/lib/utils.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Print all favorites 8 | func PrintFavorites() { 9 | for _, v := range favorites { 10 | fmt.Println(v) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ch01/loop/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | sum() 7 | sum1() 8 | } 9 | func sum() { 10 | sum := 0 11 | for i := 0; i < 10; i++ { 12 | sum += i 13 | } 14 | fmt.Println(sum) 15 | } 16 | func sum1() { 17 | sum := 1 18 | for sum < 50 { 19 | sum += sum 20 | } 21 | fmt.Println(sum) 22 | } 23 | -------------------------------------------------------------------------------- /ch01/strutils/utils.go: -------------------------------------------------------------------------------- 1 | // Package strutils provides string utility functions 2 | package strutils 3 | 4 | import ( 5 | "strings" 6 | "unicode" 7 | ) 8 | 9 | // Returns the string changed with upper case. 10 | func ToUpperCase(s string) string { 11 | return strings.ToUpper(s) 12 | } 13 | 14 | // Returns the string changed with lower case. 15 | func ToLowerCase(s string) string { 16 | return strings.ToLower(s) 17 | } 18 | 19 | // Returns the string changed to upper case for its first letter. 20 | func ToFirstUpper(s string) string { 21 | if len(s) < 1 { // if the empty string 22 | return s 23 | } 24 | // Trim the string 25 | t := strings.Trim(s, " ") 26 | // Convert all letters to lower case 27 | t = strings.ToLower(t) 28 | res := []rune(t) 29 | // Convert first letter to upper case 30 | res[0] = unicode.ToUpper(res[0]) 31 | return string(res) 32 | } 33 | -------------------------------------------------------------------------------- /ch01/strutilsdemo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/shijuvar/go-recipes/ch01/strutils" 7 | ) 8 | 9 | func main() { 10 | str1, str2 := "Golang", "gopher" 11 | // Convert to upper case 12 | fmt.Println("To Upper Case:", strutils.ToUpperCase(str1)) 13 | 14 | // Convert to lower case 15 | fmt.Println("To Lower Case:", strutils.ToLowerCase(str1)) 16 | 17 | // Convert first letter to upper case 18 | fmt.Println("To First Upper:", strutils.ToFirstUpper(str2)) 19 | } 20 | -------------------------------------------------------------------------------- /ch01/typeconv/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func main() { 9 | // type conversion: dealing with a type 10 | // The expression T(v) converts the value v to the type T 11 | i := 100 12 | f := float64(i) 13 | fmt.Println(reflect.TypeOf(f)) 14 | // type assertion: dealing with an interface 15 | // A type assertion provides access to an interface value's underlying concrete value 16 | var x interface{} = 100 //float64(100) 17 | y := x.(float64) 18 | fmt.Println(reflect.TypeOf(y)) 19 | } 20 | -------------------------------------------------------------------------------- /ch01/vetting/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | floatValue:=4.99 7 | fmt.Printf("The value is: %d",floatValue) 8 | } 9 | -------------------------------------------------------------------------------- /ch02/arrays/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | // Declare arrays 9 | var x [5]int 10 | // Assign values at specific index 11 | x[0] = 5 12 | x[4] = 25 13 | fmt.Println("Value of x:", x) 14 | 15 | x[1] = 10 16 | x[2] = 15 17 | x[3] = 20 18 | fmt.Println("Value of x:", x) 19 | 20 | // Declare and initialize array with array literal 21 | y := [5]int{10, 20, 30, 40, 50} 22 | fmt.Println("Value of y:", y) 23 | fmt.Println("Length of y:", len(y)) 24 | 25 | // Array literal with ... 26 | z := [...]int{10, 20, 30, 40, 50} 27 | fmt.Println("Value of z:", z) 28 | fmt.Println("Length of z:", len(z)) 29 | 30 | // Initialize values at specific index with array literal 31 | langs := [4]string{0: "Go", 3: "Julia"} 32 | fmt.Println("Value of langs:", langs) 33 | // Assign values to remain positions 34 | langs[1] = "Rust" 35 | langs[2] = "Scala" 36 | 37 | // Iterate over the elements of array 38 | fmt.Println("Value of langs:", langs) 39 | fmt.Println("\nIterate over arrays\n") 40 | for i := 0; i < len(langs); i++ { 41 | fmt.Printf("langs[%d]:%s \n", i, langs[i]) 42 | } 43 | fmt.Println("\n") 44 | 45 | // Iterate over the elements of array using range 46 | for k, v := range langs { 47 | fmt.Printf("langs[%d]:%s \n", k, v) 48 | } 49 | for k := range langs { 50 | fmt.Printf("Index:%d \n", k) 51 | } 52 | for _, v := range langs { 53 | fmt.Printf("Value:%s \n", v) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ch02/defer/deferfunc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | ) 8 | 9 | func ReadFile(filename string) ([]byte, error) { 10 | f, err := os.Open(filename) 11 | if err != nil { 12 | panic(err) 13 | } 14 | defer f.Close() 15 | return ioutil.ReadAll(f) 16 | } 17 | 18 | func main() { 19 | f, _ := ReadFile("test.txt") 20 | fmt.Println("%s", f) 21 | fmt.Println(string(f)) 22 | } 23 | -------------------------------------------------------------------------------- /ch02/defer/panicrecover.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func panicRecover() { 8 | 9 | defer fmt.Println("Deferred call - 1") 10 | defer func() { 11 | fmt.Println("Deferred call - 2") 12 | if e := recover(); e != nil { 13 | // e is the value passed to panic() 14 | fmt.Println("Recover with: ", e) 15 | } 16 | }() 17 | panic("Just panicking for the sake of example") 18 | fmt.Println("This will never be called") 19 | } 20 | 21 | func main() { 22 | fmt.Println("Starting to panic") 23 | panicRecover() 24 | fmt.Println("Program regains control after the panic recovery") 25 | } 26 | -------------------------------------------------------------------------------- /ch02/defer/test.txt: -------------------------------------------------------------------------------- 1 | this is a sample text -------------------------------------------------------------------------------- /ch02/functions/calc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func Add(x, y int) int { 8 | return x + y 9 | } 10 | 11 | func Subtract(x, y int) int { 12 | return x - y 13 | } 14 | 15 | func main() { 16 | x, y := 20, 10 17 | 18 | result := Add(x, y) 19 | fmt.Println("[Add]:", result) 20 | 21 | result = Subtract(x, y) 22 | fmt.Println("[Subtract]:", result) 23 | } 24 | -------------------------------------------------------------------------------- /ch02/functions/closures.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func SplitValues(f func(sum int) (int, int)) { 8 | x, y := f(35) 9 | fmt.Println(x, y) 10 | 11 | x, y = f(50) 12 | fmt.Println(x, y) 13 | } 14 | 15 | func main() { 16 | a, b := 5, 8 17 | fn := func(sum int) (int, int) { 18 | x := sum * a / b 19 | y := sum - x 20 | return x, y 21 | } 22 | 23 | // Passing function value as an argument to another function 24 | SplitValues(fn) 25 | 26 | // Calling the function value by providing argument 27 | x, y := fn(20) 28 | fmt.Println(x, y) 29 | } 30 | -------------------------------------------------------------------------------- /ch02/functions/swap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func Swap(x, y string) (string, string) { 8 | return y, x 9 | } 10 | 11 | func main() { 12 | x, y := "Shiju", "Varghese" 13 | fmt.Println("Before Swap:", x, y) 14 | 15 | x, y = Swap(x, y) 16 | fmt.Println("After Swap:", x, y) 17 | } 18 | -------------------------------------------------------------------------------- /ch02/functions/variadic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func Sum(nums ...int) int { 8 | total := 0 9 | for _, num := range nums { 10 | total += num 11 | } 12 | return total 13 | } 14 | 15 | func main() { 16 | // Providing four arguments 17 | total := Sum(1, 2, 3, 4) 18 | fmt.Println("The Sum is:", total) 19 | 20 | // Providing three arguments 21 | total = Sum(5, 7, 8) 22 | fmt.Println("The Sum is:", total) 23 | 24 | // Providing a Slice as an argument 25 | nums := []int{1, 2, 3, 4, 5} 26 | total = Sum(nums...) 27 | fmt.Println("The Sum is:", total) 28 | } 29 | -------------------------------------------------------------------------------- /ch02/maps/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | // Declares a nil map 9 | var chapts map[int]string 10 | 11 | // Initialize map with make function 12 | chapts = make(map[int]string) 13 | 14 | // Add data as key/value pairs 15 | chapts[1] = "Beginning Go" 16 | chapts[2] = "Go Fundamentals" 17 | chapts[3] = "Structs and Interfaces" 18 | 19 | // Iterate over the elements of map using range 20 | for k, v := range chapts { 21 | fmt.Printf("Key: %d Value: %s\n", k, v) 22 | } 23 | 24 | // Declare and initialize map using map literal 25 | langs := map[string]string{ 26 | "EL": "Greek", 27 | "EN": "English", 28 | "ES": "Spanish", 29 | "FR": "French", 30 | "HI": "Hindi", 31 | } 32 | 33 | // Delete an element 34 | delete(langs, "EL") 35 | 36 | // Lookout an element with key 37 | if lan, ok := langs["EL"]; ok { 38 | fmt.Println(lan) 39 | } else { 40 | fmt.Println("\nKey doesn't exists") 41 | } 42 | // Passing a map to function doesn't make a copy 43 | removeLan(langs, "HI") 44 | for k, v := range langs { 45 | fmt.Printf("Key: %s Value: %s\n", k, v) 46 | } 47 | } 48 | func removeLan(langs map[string]string, key string) { 49 | delete(langs, key) 50 | } 51 | -------------------------------------------------------------------------------- /ch02/maps/sort_map.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | func main() { 9 | // Initialize map with make function 10 | chapts := make(map[int]string) 11 | 12 | // Add data as key/value pairs 13 | chapts[1] = "Beginning Go" 14 | chapts[2] = "Go Fundamentals" 15 | chapts[3] = "Structs and Interfaces" 16 | for k, v := range chapts { 17 | fmt.Println(k, v) 18 | } 19 | 20 | // Slice for specifying the order of the map 21 | var keys []int 22 | // Appending keys of the map 23 | for k := range chapts { 24 | keys = append(keys, k) 25 | } 26 | // Ints sorts a slice of ints in increasing order. 27 | sort.Ints(keys) 28 | // Iterate over the map with an order 29 | for _, k := range keys { 30 | fmt.Println("Key:", k, "Value:", chapts[k]) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ch02/slices/append.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | x := make([]int, 2, 5) 9 | x[0] = 10 10 | x[1] = 20 11 | fmt.Println("Slice x:", x) 12 | fmt.Printf("Length is %d Capacity is %d\n", len(x), cap(x)) 13 | // Create a bigger slice 14 | x = append(x, 30, 40, 50) 15 | fmt.Println("Slice x after appending data:", x) 16 | fmt.Printf("Length is %d Capacity is %d\n", len(x), cap(x)) 17 | 18 | x = append(x, 60, 70, 80) 19 | fmt.Println("Slice x after appending data for the second time:", x) 20 | fmt.Printf("Length is %d Capacity is %d\n", len(x), cap(x)) 21 | 22 | } 23 | -------------------------------------------------------------------------------- /ch02/slices/append_nilslice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // Declare a nil slice 7 | var x []int 8 | fmt.Println(x, len(x), cap(x)) 9 | x = append(x, 10, 20, 30) 10 | fmt.Println("Slice x after appending data:", x) 11 | } 12 | -------------------------------------------------------------------------------- /ch02/slices/copy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | x := []int{10, 20, 30} 9 | fmt.Printf("[Slice:x] Length is %d Capacity is %d\n", len(x), cap(x)) 10 | // Create a bigger slice 11 | y := make([]int, 5, 10) 12 | copy(y, x) 13 | fmt.Printf("[Slice:y] Length is %d Capacity is %d\n", len(y), cap(y)) 14 | fmt.Println("Slice y after copying:", y) 15 | y[3] = 40 16 | y[4] = 50 17 | fmt.Println("Slice y after adding elements:", y) 18 | } 19 | -------------------------------------------------------------------------------- /ch02/slices/iterate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | x := []int{10, 20, 30, 40, 50} 9 | for k, v := range x { 10 | fmt.Printf("x[%d]: %d\n", k, v) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ch02/slices/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | x := make([]int, 3, 5) 9 | x[0] = 10 10 | x[1] = 20 11 | x[2] = 30 12 | fmt.Println(x) 13 | fmt.Println(len(x)) 14 | fmt.Println(cap(x)) 15 | 16 | y := make([]int, 3) 17 | y[0] = 10 18 | y[1] = 20 19 | y[2] = 30 20 | fmt.Println(y) 21 | fmt.Println(len(y)) 22 | fmt.Println(cap(y)) 23 | 24 | z := []int{10, 20, 30} 25 | fmt.Println(len(z)) 26 | fmt.Println(cap(z)) 27 | 28 | z1 := []int{0: 10, 2: 30} 29 | fmt.Println(len(z1)) 30 | fmt.Println(cap(z1)) 31 | 32 | x1 := []int{10, 20, 30} 33 | y1 := append(x1, 40, 50) 34 | fmt.Println(x1, y1) 35 | } 36 | -------------------------------------------------------------------------------- /ch02/slices/slicing.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | x := []int{10, 20, 30, 40, 50} 9 | y := x[1:3] 10 | fmt.Println("y:", y) 11 | fmt.Println(len(y)) 12 | fmt.Println(cap(y)) 13 | 14 | z := x[:3] 15 | fmt.Println("z:", z) 16 | fmt.Println(len(z)) 17 | fmt.Println(cap(z)) 18 | x1 := x[:] 19 | fmt.Println("x1:", x1) 20 | fmt.Println(len(x1)) 21 | fmt.Println(cap(x1)) 22 | 23 | x1[4] = 75 24 | fmt.Println("x:", x) 25 | fmt.Println("x1:", x1) 26 | 27 | } 28 | -------------------------------------------------------------------------------- /ch03/ecommerce/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | order := &Order{ 10 | Id: 1001, 11 | Customer: Customer{ 12 | FirstName: "Alex", 13 | LastName: "John", 14 | Email: "alex@email.com", 15 | Phone: "732-757-2923", 16 | Addresses: []Address{ 17 | Address{ 18 | Street: "1 Mission Street", 19 | City: "San Francisco", 20 | State: "CA", 21 | Zip: "94105", 22 | IsShippingAddress: true, 23 | }, 24 | Address{ 25 | Street: "49 Stevenson Street", 26 | City: "San Francisco", 27 | State: "CA", 28 | Zip: "94105", 29 | }, 30 | }, 31 | }, 32 | Status: "Placed", 33 | PlacedOn: time.Date(2016, time.April, 10, 0, 0, 0, 0, time.UTC), 34 | OrderItems: []OrderItem{ 35 | OrderItem{ 36 | Product: Product{ 37 | Code: "knd100", 38 | Name: "Kindle Voyage", 39 | Description: "Kindle Voyage Wifi, 6 High-Resolution Display", 40 | UnitPrice: 220, 41 | }, 42 | Quantity: 1, 43 | }, 44 | OrderItem{ 45 | Product: Product{ 46 | Code: "fint101", 47 | Name: "Kindle Case", 48 | Description: "Fintie Kindle Voyage SmartShell Case", 49 | UnitPrice: 10, 50 | }, 51 | Quantity: 2, 52 | }, 53 | }, 54 | } 55 | 56 | fmt.Println(order.ToString()) 57 | // Change Order status 58 | order.ChangeStatus("Processing") 59 | fmt.Println("\n") 60 | fmt.Println(order.ToString()) 61 | } 62 | -------------------------------------------------------------------------------- /ch03/ecommerce/models.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type Address struct { 9 | Street, City, State, Zip string 10 | IsShippingAddress bool 11 | } 12 | 13 | type Customer struct { 14 | FirstName, LastName, Email, Phone string 15 | Addresses []Address 16 | } 17 | 18 | func (c Customer) ToString() string { 19 | return fmt.Sprintf("Customer: %s %s, Email:%s", c.FirstName, c.LastName, c.Email) 20 | } 21 | func (c Customer) ShippingAddress() string { 22 | for _, v := range c.Addresses { 23 | if v.IsShippingAddress == true { 24 | return fmt.Sprintf("%s, %s, %s, Zip - %s", v.Street, v.City, v.State, v.Zip) 25 | } 26 | } 27 | return "" 28 | } 29 | 30 | type Order struct { 31 | Id int 32 | Customer 33 | PlacedOn time.Time 34 | Status string 35 | OrderItems []OrderItem 36 | } 37 | 38 | func (o *Order) GrandTotal() float64 { 39 | var total float64 40 | for _, v := range o.OrderItems { 41 | total += v.Total() 42 | } 43 | return total 44 | } 45 | func (o *Order) ToString() string { 46 | var orderStr string 47 | orderStr = fmt.Sprintf("Order#:%d, OrderDate:%s, Status:%s, Grand Total:%f\n", o.Id, o.PlacedOn, o.Status, o.GrandTotal()) 48 | orderStr += o.Customer.ToString() 49 | orderStr += fmt.Sprintf("\nOrder Items:") 50 | for _, v := range o.OrderItems { 51 | orderStr += fmt.Sprintf("\n") 52 | orderStr += v.ToString() 53 | } 54 | orderStr += fmt.Sprintf("\nShipping Address:") 55 | orderStr += o.Customer.ShippingAddress() 56 | return orderStr 57 | } 58 | func (o *Order) ChangeStatus(newStatus string) { 59 | o.Status = newStatus 60 | } 61 | 62 | type OrderItem struct { 63 | Product 64 | Quantity int 65 | } 66 | 67 | func (item OrderItem) Total() float64 { 68 | return float64(item.Quantity) * item.Product.UnitPrice 69 | } 70 | func (item OrderItem) ToString() string { 71 | itemStr := fmt.Sprintf("Code:%s, Product:%s -- %s, UnitPrice:%f, Quantity:%d, Total:%f", 72 | item.Product.Code, item.Product.Name, item.Product.Description, item.Product.UnitPrice, item.Quantity, item.Total()) 73 | return itemStr 74 | 75 | } 76 | 77 | type Product struct { 78 | Code, Name, Description string 79 | UnitPrice float64 80 | } 81 | -------------------------------------------------------------------------------- /ch03/employee/employee.go: -------------------------------------------------------------------------------- 1 | // Example program with Interface, Composition and Method Overriding 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type TeamMember interface { 10 | PrintName() 11 | PrintDetails() 12 | } 13 | 14 | type Employee struct { 15 | FirstName, LastName string 16 | Dob time.Time 17 | JobTitle, Location string 18 | } 19 | 20 | func (e Employee) PrintName() { 21 | fmt.Printf("\n%s %s\n", e.FirstName, e.LastName) 22 | } 23 | 24 | func (e Employee) PrintDetails() { 25 | fmt.Printf("Date of Birth: %s, Job: %s, Location: %s\n", e.Dob.String(), e.JobTitle, e.Location) 26 | } 27 | 28 | type Developer struct { 29 | Employee //type embedding for composition 30 | Skills []string 31 | } 32 | 33 | // Overrides the PrintDetails 34 | func (d Developer) PrintDetails() { 35 | // Call Employee PrintDetails 36 | d.Employee.PrintDetails() 37 | fmt.Println("Technical Skills:") 38 | for _, v := range d.Skills { 39 | fmt.Println(v) 40 | } 41 | } 42 | 43 | type Manager struct { 44 | Employee //type embedding for composition 45 | Projects []string 46 | Locations []string 47 | } 48 | 49 | // Overrides the PrintDetails 50 | func (m Manager) PrintDetails() { 51 | // Call Employee PrintDetails 52 | m.Employee.PrintDetails() 53 | fmt.Println("Projects:") 54 | for _, v := range m.Projects { 55 | fmt.Println(v) 56 | } 57 | fmt.Println("Managing teams for the locations:") 58 | for _, v := range m.Locations { 59 | fmt.Println(v) 60 | } 61 | } 62 | 63 | type Team struct { 64 | Name, Description string 65 | TeamMembers []TeamMember 66 | } 67 | 68 | func (t Team) PrintTeamDetails() { 69 | fmt.Printf("Team: %s - %s\n", t.Name, t.Description) 70 | fmt.Println("Details of the team members:") 71 | for _, v := range t.TeamMembers { 72 | v.PrintName() 73 | v.PrintDetails() 74 | } 75 | } 76 | 77 | func main() { 78 | steve := Developer{ 79 | Employee: Employee{ 80 | FirstName: "Steve", 81 | LastName: "John", 82 | Dob: time.Date(1990, time.February, 17, 0, 0, 0, 0, time.UTC), 83 | JobTitle: "Software Engineer", 84 | Location: "San Fancisco", 85 | }, 86 | Skills: []string{"Go", "Docker", "Kubernetes"}, 87 | } 88 | irene := Developer{ 89 | Employee: Employee{ 90 | FirstName: "Irene", 91 | LastName: "Rose", 92 | Dob: time.Date(1991, time.January, 13, 0, 0, 0, 0, time.UTC), 93 | JobTitle: "Software Engineer", 94 | Location: "Santa Clara", 95 | }, 96 | Skills: []string{"Go", "MongoDB"}, 97 | } 98 | alex := Manager{ 99 | Employee: Employee{ 100 | FirstName: "Alex", 101 | LastName: "Williams", 102 | Dob: time.Date(1979, time.February, 17, 0, 0, 0, 0, time.UTC), 103 | JobTitle: "Program Manger", 104 | Location: "Santa Clara", 105 | }, 106 | Projects: []string{"CRM", "e-Commerce"}, 107 | Locations: []string{"San Fancisco", "Santa Clara"}, 108 | } 109 | 110 | // Create team 111 | team := Team{ 112 | "Go", 113 | "Golang Engineering Team", 114 | []TeamMember{steve, irene, alex}, 115 | } 116 | // Get details of Team 117 | team.PrintTeamDetails() 118 | } 119 | -------------------------------------------------------------------------------- /ch03/person.go: -------------------------------------------------------------------------------- 1 | // Person struct with methods of pointer receiver 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | // Person struct 11 | type Person struct { 12 | FirstName, LastName string 13 | Dob time.Time 14 | Email, Location string 15 | } 16 | 17 | // PrintName prints the name of the Person 18 | func (p Person) PrintName() { 19 | fmt.Printf("\n%s %s\n", p.FirstName, p.LastName) 20 | } 21 | 22 | // PrintDetails prints the details of Person 23 | func (p Person) PrintDetails() { 24 | fmt.Printf("[Date of Birth: %s, Email: %s, Location: %s ]\n", p.Dob.String(), p.Email, p.Location) 25 | } 26 | 27 | func main() { 28 | /* 29 | // Declare a Person variable using var 30 | var p Person 31 | // Assign values to fields 32 | p.FirstName="Shiju" 33 | p.LastName="Varghese" 34 | p.Dob= time.Date(1979, time.February, 17, 0, 0, 0, 0, time.UTC) 35 | p.Email="shiju@email.com" 36 | p.Location= "Kochi" 37 | 38 | // Declare a Person variable and initialize values using Struct literal 39 | p := Person{ 40 | "Shiju", 41 | "Varghese", 42 | time.Date(1979, time.February, 17, 0, 0, 0, 0, time.UTC), 43 | "shiju@email.com", 44 | "Kochi", 45 | } 46 | 47 | */ 48 | p := Person{ 49 | FirstName: "Shiju", 50 | LastName: "Varghese", 51 | Dob: time.Date(1979, time.February, 17, 0, 0, 0, 0, time.UTC), 52 | Email: "shiju@email.com", 53 | Location: "Kochi", 54 | } 55 | p.PrintName() 56 | p.PrintDetails() 57 | 58 | } 59 | -------------------------------------------------------------------------------- /ch03/pointer/main.go: -------------------------------------------------------------------------------- 1 | // Person struct with methods of pointer receiver 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | type Person struct { 11 | FirstName, LastName string 12 | Dob time.Time 13 | Email, Location string 14 | } 15 | 16 | //A person method with pointer receiver 17 | func (p *Person) PrintName() { 18 | fmt.Printf("\n%s %s\n", p.FirstName, p.LastName) 19 | } 20 | 21 | //A person method with pointer receiver 22 | func (p *Person) PrintDetails() { 23 | fmt.Printf("[Date of Birth: %s, Email: %s, Location: %s ]\n", p.Dob.String(), p.Email, p.Location) 24 | } 25 | 26 | //A person method with pointer receiver 27 | func (p *Person) ChangeLocation(newLocation string) { 28 | p.Location = newLocation 29 | } 30 | func main() { 31 | p := &Person{ 32 | "Shiju", 33 | "Varghese", 34 | time.Date(1979, time.February, 17, 0, 0, 0, 0, time.UTC), 35 | "shiju@email.com", 36 | "Kochi", 37 | } 38 | p.ChangeLocation("Santa Clara") 39 | p.PrintName() 40 | p.PrintDetails() 41 | 42 | } 43 | -------------------------------------------------------------------------------- /ch04/channels/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | // Declare a unbuffered channel 9 | counter := make(chan int) 10 | // Creates a buffered channel with capacity of 3 11 | nums := make(chan int, 3) 12 | go func() { 13 | // Send value to the unbuffered channel 14 | counter <- 1 15 | close(counter) // Closes the channel 16 | }() 17 | 18 | go func() { 19 | // Send values to the buffered channel 20 | nums <- 10 21 | nums <- 30 22 | nums <- 50 23 | }() 24 | // Read the value from unbuffered channel 25 | fmt.Println(<-counter) 26 | val, ok := <-counter // Trying to read from closed channel 27 | if ok { 28 | fmt.Println(val) // This won't execute 29 | } 30 | // Read the 3 buffered values from the buffered channel 31 | fmt.Println(<-nums) 32 | fmt.Println(<-nums) 33 | fmt.Println(<-nums) 34 | close(nums) // Closes the channel 35 | } 36 | -------------------------------------------------------------------------------- /ch04/deadlock/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | // Declare a unbuffered channel 9 | counter := make(chan int) 10 | // Perform send operation by launching new goroutine 11 | go func() { 12 | counter <- 10 13 | }() 14 | fmt.Println(<-counter) // Receive operation from the channel 15 | } 16 | -------------------------------------------------------------------------------- /ch04/deadlock/main_deadlock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | // Declare a unbuffered channel 9 | counter := make(chan int) 10 | // This will create a deadlock 11 | counter <- 10 // Send operation to a channel from main goroutine 12 | fmt.Println(<-counter) // Receive operation from the channel 13 | } 14 | -------------------------------------------------------------------------------- /ch04/mathtable/main.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to create goroutines 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "math/rand" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | // WaitGroup is used to wait for the program to finish goroutines. 12 | var wg sync.WaitGroup 13 | 14 | func main() { 15 | 16 | // Add a count of two, one for each goroutine. 17 | wg.Add(2) 18 | 19 | fmt.Println("Start Goroutines") 20 | // Launch functions as goroutines 21 | go addTable() 22 | go multiTable() 23 | // Wait for the goroutines to finish. 24 | fmt.Println("Waiting To Finish") 25 | wg.Wait() 26 | fmt.Println("\nTerminating Program") 27 | } 28 | 29 | func addTable() { 30 | // Schedule the call to WaitGroup's Done to tell goroutine is completed. 31 | defer wg.Done() 32 | for i := 1; i <= 10; i++ { 33 | sleep := rand.Int63n(1000) 34 | time.Sleep(time.Duration(sleep) * time.Millisecond) 35 | fmt.Println("Addition Table for:", i) 36 | for j := 1; j <= 10; j++ { 37 | //res = i + j 38 | fmt.Printf("%d+%d=%d\t", i, j, i+j) 39 | } 40 | fmt.Println("\n") 41 | } 42 | } 43 | func multiTable() { 44 | // Schedule the call to WaitGroup's Done to tell goroutine is completed. 45 | defer wg.Done() 46 | for i := 1; i <= 10; i++ { 47 | sleep := rand.Int63n(1000) 48 | time.Sleep(time.Duration(sleep) * time.Millisecond) 49 | fmt.Println("Multiplication Table for:", i) 50 | for j := 1; j <= 10; j++ { 51 | //res = i + j 52 | fmt.Printf("%d*%d=%d\t", i, j, i*j) 53 | } 54 | fmt.Println("\n") 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ch04/pipeline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/rand" 7 | "sync" 8 | ) 9 | 10 | type fibvalue struct { 11 | input, value int 12 | } 13 | 14 | var wg sync.WaitGroup 15 | 16 | func randomCounter(out chan<- int) { 17 | defer wg.Done() 18 | var random int 19 | for x := 0; x < 10; x++ { 20 | random = rand.Intn(50) 21 | out <- random 22 | } 23 | close(out) 24 | } 25 | 26 | func generateFibonacci(out chan<- fibvalue, in <-chan int) { 27 | defer wg.Done() 28 | var input float64 29 | for v := range in { 30 | input = float64(v) 31 | // Fibonacci using Binet's formula 32 | Phi := (1 + math.Sqrt(5)) / 2 33 | phi := (1 - math.Sqrt(5)) / 2 34 | result := (math.Pow(Phi, input) - math.Pow(phi, input)) / math.Sqrt(5) 35 | out <- fibvalue{ 36 | input: v, 37 | value: int(result), 38 | } 39 | } 40 | close(out) 41 | } 42 | 43 | func printFibonacci(in <-chan fibvalue) { 44 | defer wg.Done() 45 | for v := range in { 46 | fmt.Printf("Fibonacci value of %d is %d\n", v.input, v.value) 47 | } 48 | } 49 | 50 | func main() { 51 | // Add 3 into WaitGroup Counter 52 | wg.Add(3) 53 | // Declare Channels 54 | randoms := make(chan int) 55 | fibs := make(chan fibvalue) 56 | // Launching 3 goroutines 57 | go randomCounter(randoms) 58 | go generateFibonacci(fibs, randoms) 59 | go printFibonacci(fibs) 60 | // Wait for completing all goroutines 61 | wg.Wait() 62 | } 63 | -------------------------------------------------------------------------------- /ch04/pipeline/main1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/rand" 7 | "sync" 8 | ) 9 | 10 | type fibvalue struct { 11 | input, value int 12 | } 13 | 14 | var wg sync.WaitGroup 15 | 16 | // Generates random values 17 | func randomCounter(out chan int) { 18 | defer wg.Done() 19 | var random int 20 | for x := 0; x < 10; x++ { 21 | random = rand.Intn(50) 22 | out <- random 23 | } 24 | close(out) 25 | } 26 | 27 | // Produces fibonacci values of inputs provided by randomCounter 28 | func generateFibonacci(out chan fibvalue, in chan int) { 29 | defer wg.Done() 30 | var input float64 31 | for v := range in { 32 | input = float64(v) 33 | // Fibonacci using Binet's formula 34 | Phi := (1 + math.Sqrt(5)) / 2 35 | phi := (1 - math.Sqrt(5)) / 2 36 | result := (math.Pow(Phi, input) - math.Pow(phi, input)) / math.Sqrt(5) 37 | out <- fibvalue{ 38 | input: v, 39 | value: int(result), 40 | } 41 | } 42 | close(out) 43 | } 44 | 45 | // Print fibonacci values generated by generateFibonacci 46 | func printFibonacci(in chan fibvalue) { 47 | defer wg.Done() 48 | for v := range in { 49 | fmt.Printf("Fibonacci value of %d is %d\n", v.input, v.value) 50 | } 51 | } 52 | 53 | func main() { 54 | // Add 3 into WaitGroup Counter 55 | wg.Add(3) 56 | // Declare Channels 57 | randoms := make(chan int) 58 | fibs := make(chan fibvalue) 59 | // Launching 3 goroutines 60 | go randomCounter(randoms) // First stage of pipeline 61 | go generateFibonacci(fibs, randoms) // Second stage of pipeline 62 | go printFibonacci(fibs) // Third stage of pipeline 63 | // Wait for completing all goroutines 64 | wg.Wait() 65 | } 66 | -------------------------------------------------------------------------------- /ch04/select/context.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | func generateValues(ctx context.Context, counter chan int) { 9 | n := 1 10 | for { 11 | select { 12 | case <-ctx.Done(): 13 | return 14 | case counter <- n: 15 | n++ 16 | } 17 | } 18 | } 19 | 20 | func main() { 21 | // WithCancel returns a copy of parent with a new Done channel. The returned 22 | // context's Done channel is closed when the returned cancel function is called 23 | // or when the parent context's Done channel is closed, whichever happens first. 24 | // 25 | // Canceling this context releases resources associated with it, so code should 26 | // call cancel as soon as the operations running in this Context complete. 27 | ctx, cancel := context.WithCancel(context.Background()) 28 | // ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 29 | counter := make(chan int) 30 | defer cancel() 31 | go generateValues(ctx, counter) 32 | for n := range counter { 33 | fmt.Println(n) 34 | if n == 10 { 35 | cancel() 36 | break 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ch04/select/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/rand" 7 | "sync" 8 | ) 9 | 10 | type ( 11 | fibvalue struct { 12 | input, value int 13 | } 14 | squarevalue struct { 15 | input, value int 16 | } 17 | ) 18 | 19 | func generateSquare(sqrs chan<- squarevalue) { 20 | defer wg.Done() 21 | for i := 1; i <= 10; i++ { 22 | num := rand.Intn(50) 23 | sqrs <- squarevalue{ 24 | input: num, 25 | value: num * num, 26 | } 27 | } 28 | } 29 | func generateFibonacci(fibs chan<- fibvalue) { 30 | defer wg.Done() 31 | for i := 1; i <= 10; i++ { 32 | num := float64(rand.Intn(50)) 33 | // Fibonacci using Binet's formula 34 | Phi := (1 + math.Sqrt(5)) / 2 35 | phi := (1 - math.Sqrt(5)) / 2 36 | result := (math.Pow(Phi, num) - math.Pow(phi, num)) / math.Sqrt(5) 37 | fibs <- fibvalue{ 38 | input: int(num), 39 | value: int(result), 40 | } 41 | } 42 | } 43 | func printValues(fibs <-chan fibvalue, sqrs <-chan squarevalue) { 44 | defer wg.Done() 45 | for i := 1; i <= 20; i++ { 46 | select { 47 | case fib := <-fibs: 48 | fmt.Printf("Fibonacci value of %d is %d\n", fib.input, fib.value) 49 | case sqr := <-sqrs: 50 | fmt.Printf("Square value of %d is %d\n", sqr.input, sqr.value) 51 | } 52 | } 53 | } 54 | 55 | // wg is used to wait for the program to finish. 56 | var wg sync.WaitGroup 57 | 58 | func main() { 59 | wg.Add(3) 60 | // Create Channels 61 | fibs := make(chan fibvalue) 62 | sqrs := make(chan squarevalue) 63 | // Launching 3 goroutines 64 | go generateFibonacci(fibs) 65 | go generateSquare(sqrs) 66 | go printValues(fibs, sqrs) 67 | // Wait for completing all goroutines 68 | wg.Wait() 69 | } 70 | -------------------------------------------------------------------------------- /ch04/unbuffercounter/main.go: -------------------------------------------------------------------------------- 1 | // Example program with unbuffered channel 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "sync" 7 | ) 8 | 9 | // wg is used to wait for the program to finish. 10 | var wg sync.WaitGroup 11 | 12 | func main() { 13 | 14 | count := make(chan int) 15 | // Add a count of two, one for each goroutine. 16 | wg.Add(2) 17 | 18 | fmt.Println("Start Goroutines") 19 | // Launch a goroutine with label "Goroutine-1" 20 | go printCounts("Goroutine-1", count) 21 | // Launch a goroutine with label "Goroutine-2" 22 | go printCounts("Goroutine-2", count) 23 | fmt.Println("Communication of channel begins") 24 | count <- 1 25 | // Wait for the goroutines to finish. 26 | fmt.Println("Waiting To Finish") 27 | wg.Wait() 28 | fmt.Println("\nTerminating the Program") 29 | } 30 | 31 | func printCounts(label string, count chan int) { 32 | // Schedule the call to WaitGroup's Done to tell goroutine is completed. 33 | defer wg.Done() 34 | for val := range count { 35 | fmt.Printf("Count: %d received from %s \n", val, label) 36 | if val == 10 { 37 | fmt.Printf("Channel Closed from %s \n", label) 38 | // Close the channel 39 | close(count) 40 | return 41 | } 42 | val++ 43 | // Send count back to the other goroutine. 44 | count <- val 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ch04/unbuffercounter/main1.go: -------------------------------------------------------------------------------- 1 | // Example program with unbuffered channel 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "sync" 7 | ) 8 | 9 | // wg is used to wait for the program to finish. 10 | var wg sync.WaitGroup 11 | 12 | func main() { 13 | 14 | count := make(chan int) 15 | // Add a count of two, one for each goroutine. 16 | wg.Add(2) 17 | 18 | fmt.Println("Start Goroutines") 19 | // Launch a goroutine with label "Goroutine-1" 20 | go printCounts("Goroutine-1", count) 21 | // Launch a goroutine with label "Goroutine-2" 22 | go printCounts("Goroutine-2", count) 23 | fmt.Println("Communication of channel begins") 24 | count <- 1 25 | // Wait for the goroutines to finish. 26 | fmt.Println("Waiting To Finish") 27 | wg.Wait() 28 | fmt.Println("\nTerminating the Program") 29 | } 30 | 31 | func printCounts(label string, count chan int) { 32 | // Schedule the call to WaitGroup's Done to tell goroutine is completed. 33 | defer wg.Done() 34 | for { 35 | // Receives message from Channel 36 | val, ok := <-count 37 | if !ok { 38 | fmt.Println("Channel was closed") 39 | return 40 | } 41 | fmt.Printf("Count: %d received from %s \n", val, label) 42 | if val == 10 { 43 | fmt.Printf("Channel Closed from %s \n", label) 44 | // Close the channel 45 | close(count) 46 | return 47 | } 48 | val++ 49 | // Send count back to the other goroutine. 50 | count <- val 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ch04/worker/main.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to use a buffered 2 | // channel to work on multiple tasks with a predefined number 3 | // of goroutines. 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "math/rand" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | type Task struct { 14 | Id int 15 | JobId int 16 | Status string 17 | CreatedOn time.Time 18 | } 19 | 20 | func (t *Task) Run() { 21 | 22 | sleep := rand.Int63n(1000) 23 | // Delaying the execution for the sake of example 24 | time.Sleep(time.Duration(sleep) * time.Millisecond) 25 | t.Status = "Completed" 26 | } 27 | 28 | // wg is used to wait for the program to finish. 29 | var wg sync.WaitGroup 30 | 31 | const noOfWorkers = 3 32 | 33 | // main is the entry point for all Go programs. 34 | func main() { 35 | // Create a buffered channel to manage the task queue. 36 | taskQueue := make(chan *Task, 10) 37 | 38 | // Launch goroutines to handle the work. 39 | // The worker process is distributing with the value of noOfWorkers. 40 | wg.Add(noOfWorkers) 41 | for gr := 1; gr <= noOfWorkers; gr++ { 42 | go worker(taskQueue, gr) 43 | } 44 | 45 | // Add Tasks into Buffered channel. 46 | for i := 1; i <= 10; i++ { 47 | taskQueue <- &Task{ 48 | Id: i, 49 | JobId: 100 + i, 50 | CreatedOn: time.Now(), 51 | } 52 | } 53 | 54 | // Close the channel 55 | close(taskQueue) 56 | 57 | // Wait for all the work to get done. 58 | wg.Wait() 59 | } 60 | 61 | // worker is launched as a goroutine to process Tasks from 62 | // the buffered channel. 63 | func worker(taskQueue <-chan *Task, workerId int) { 64 | // Schedule the call to Done method of WaitGroup. 65 | defer wg.Done() 66 | for v := range taskQueue { 67 | fmt.Printf("Worker%d: received request for Task:%d - Job:%d\n", workerId, v.Id, v.JobId) 68 | v.Run() 69 | // Display we finished the work. 70 | fmt.Printf("Worker%d: Status:%s for Task:%d - Job:%d\n", workerId, v.Status, v.Id, v.JobId) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ch05/archivetar/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "archive/tar" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | ) 10 | 11 | // addToArchive writes a given file into a .tar file 12 | // Returns nill if the operation is succeeded 13 | func addToArchive(filename string, tw *tar.Writer) error { 14 | // Open the file to archive into tar file. 15 | file, err := os.Open(filename) 16 | if err != nil { 17 | return err 18 | } 19 | defer file.Close() 20 | // Get the FileInfo struct that describe the file. 21 | fileinfo, err := file.Stat() 22 | // Create a pointer to tar.Header struct 23 | hdr := &tar.Header{ 24 | ModTime: fileinfo.ModTime(), // modified time 25 | Name: filename, // name of header 26 | Size: fileinfo.Size(), // length in bytes 27 | Mode: int64(fileinfo.Mode().Perm()), // permission and mode bits 28 | } 29 | // WriteHeader writes tar.Header and prepares to accept the file's contents. 30 | if err := tw.WriteHeader(hdr); err != nil { 31 | return err 32 | } 33 | // Write the file contents to the tar file. 34 | copied, err := io.Copy(tw, file) 35 | if err != nil { 36 | return err 37 | } 38 | // Check the size of copied file with the source file. 39 | if copied < fileinfo.Size() { 40 | return fmt.Errorf("Size of the copied file doesn't match with source file %s: %s", filename, err) 41 | } 42 | return nil 43 | } 44 | 45 | // archiveFiles archives a group of given files into a tar file. 46 | func archiveFiles(files []string, archive string) error { 47 | // Flags for open the tar file. 48 | flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC 49 | // Open the tar file 50 | file, err := os.OpenFile(archive, flags, 0644) 51 | if err != nil { 52 | return err 53 | } 54 | defer file.Close() 55 | // Create a new Writer writing to given file object. 56 | // Writer provides sequential writing of a tar archive in POSIX.1 format. 57 | tw := tar.NewWriter(file) 58 | defer tw.Close() 59 | // Iterate through the files to write each file into the tar file. 60 | for _, filename := range files { 61 | // Write the file into tar file. 62 | if err := addToArchive(filename, tw); err != nil { 63 | return err 64 | } 65 | } 66 | return nil 67 | } 68 | 69 | // readArchive reads the file contents from tar file. 70 | func readArchive(archive string) error { 71 | // Open the tar archive file. 72 | file, err := os.Open(archive) 73 | if err != nil { 74 | return err 75 | } 76 | defer file.Close() 77 | // Create the tar.Reader to read the tar archive. 78 | // A Reader provides sequential access to the contents of a tar archive. 79 | tr := tar.NewReader(file) 80 | // Iterate through the files in the tar archive. 81 | for { 82 | hdr, err := tr.Next() 83 | if err == io.EOF { 84 | // End of tar archive 85 | fmt.Println("end") 86 | break 87 | } 88 | if err != nil { 89 | return err 90 | } 91 | size := hdr.Size 92 | contents := make([]byte, size) 93 | read, err := io.ReadFull(tr, contents) 94 | // Check the size of file contents 95 | if int64(read) != size { 96 | return fmt.Errorf("Size of the opened file doesn't match with the file %s", hdr.Name) 97 | } 98 | // hdr.Name returns the file name. 99 | fmt.Printf("Contents of the file %s:\n", hdr.Name) 100 | // Writing the file contents into Stdout. 101 | fmt.Fprintf(os.Stdout, "\n%s", contents) 102 | } 103 | return nil 104 | } 105 | 106 | func main() { 107 | // Name of the tar file 108 | archive := "source.tar" 109 | // Files to be archived in tar format 110 | files := []string{"main.go", "readme.txt"} 111 | // Archive files into tar format 112 | err := archiveFiles(files, archive) 113 | if err != nil { 114 | log.Fatalf("Error while writing to tar file:%s", err) 115 | } 116 | // Archiving is sucsess. 117 | fmt.Println("The tar file source.tar has been created") 118 | // Read the file contents of tar file 119 | err = readArchive(archive) 120 | if err != nil { 121 | log.Fatalf("Error while reading the tar file:%s", err) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ch05/archivetar/readme.txt: -------------------------------------------------------------------------------- 1 | Example program using archive/tar package -------------------------------------------------------------------------------- /ch05/archivetar/source.tar: -------------------------------------------------------------------------------- 1 | main.go0000666000000000000000000000654212731232767010464 ustar0000000000000000package main 2 | 3 | import ( 4 | "archive/tar" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | ) 10 | 11 | // addToArchive writes a given file into a .tar file 12 | // Returns nill if the operation is succeeded 13 | func addToArchive(filename string, tw *tar.Writer) error { 14 | // Open the file to archive into tar file. 15 | file, err := os.Open(filename) 16 | if err != nil { 17 | return err 18 | } 19 | defer file.Close() 20 | // Get the FileInfo struct that describe the file. 21 | fileinfo, err := file.Stat() 22 | // Create a pointer to tar.Header struct 23 | hdr := &tar.Header{ 24 | ModTime: fileinfo.ModTime(), // modified time 25 | Name: filename, // name of header 26 | Size: fileinfo.Size(), // length in bytes 27 | Mode: int64(fileinfo.Mode().Perm()), // permission and mode bits 28 | } 29 | // WriteHeader writes tar.Header and prepares to accept the file's contents. 30 | if err := tw.WriteHeader(hdr); err != nil { 31 | return err 32 | } 33 | // Write the file contents to the tar file. 34 | copied, err := io.Copy(tw, file) 35 | if err != nil { 36 | return err 37 | } 38 | // Check the size of copied file with the source file. 39 | if copied < fileinfo.Size() { 40 | return fmt.Errorf("Size of the copied file doesn't match with source file %s: %s", filename, err) 41 | } 42 | return nil 43 | } 44 | 45 | // archiveFiles archives a group of given files into a tar file. 46 | func archiveFiles(files []string, archive string) error { 47 | // Flags for open the tar file. 48 | flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC 49 | // Open the tar file 50 | file, err := os.OpenFile(archive, flags, 0644) 51 | if err != nil { 52 | return err 53 | } 54 | defer file.Close() 55 | // Create a new Writer writing to given file object. 56 | // Writer provides sequential writing of a tar archive in POSIX.1 format. 57 | tw := tar.NewWriter(file) 58 | defer tw.Close() 59 | // Iterate through the files to write each file into the tar file. 60 | for _, filename := range files { 61 | // Write the file into tar file. 62 | if err := addToArchive(filename, tw); err != nil { 63 | return err 64 | } 65 | } 66 | return nil 67 | } 68 | 69 | // readArchive reads the file contents from tar file. 70 | func readArchive(archive string) error { 71 | // Open the tar archive file. 72 | file, err := os.Open(archive) 73 | if err != nil { 74 | return err 75 | } 76 | defer file.Close() 77 | // Create the tar.Reader to read the tar archive. 78 | // A Reader provides sequential access to the contents of a tar archive. 79 | tr := tar.NewReader(file) 80 | // Iterate through the files in the tar archive. 81 | for { 82 | hdr, err := tr.Next() 83 | if err == io.EOF { 84 | // End of tar archive 85 | fmt.Println("end") 86 | break 87 | } 88 | if err != nil { 89 | return err 90 | } 91 | size := hdr.Size 92 | contents := make([]byte, size) 93 | read, err := io.ReadFull(tr, contents) 94 | // Check the size of file contents 95 | if int64(read) != size { 96 | return fmt.Errorf("Size of the opened file doesn't match with the file %s", hdr.Name) 97 | } 98 | fmt.Printf("Contents of the file%s:\n", hdr.Name) 99 | // Writing the file contents into Stdout. 100 | fmt.Fprintf(os.Stdout, "\n%s", contents) 101 | } 102 | return nil 103 | } 104 | 105 | func main() { 106 | // Name of the tar file 107 | archive := "source.tar" 108 | // Files to be archived in tar format 109 | files := []string{"main.go", "readme.txt"} 110 | // Archive files into tar format 111 | err := archiveFiles(files, archive) 112 | if err != nil { 113 | log.Fatalf("Error while writing to tar file:%s", err) 114 | } 115 | // Archiving is sucsess. 116 | fmt.Println("The tar file source.tar has been created") 117 | // Read the file contents of tar file 118 | err = readArchive(archive) 119 | if err != nil { 120 | log.Fatalf("Error while reading the tar file:%s", err) 121 | } 122 | } 123 | readme.txt0000666000000000000000000000005112730431524011163 ustar0000000000000000Example program using archive/tar package -------------------------------------------------------------------------------- /ch05/archivezip/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "archive/zip" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | ) 10 | 11 | // addToArchive writes a given file into a zip file. 12 | func addToArchive(filename string, zw *zip.Writer) error { 13 | // Open the given file to archive into a zip file. 14 | file, err := os.Open(filename) 15 | if err != nil { 16 | return err 17 | } 18 | defer file.Close() 19 | // Create adds a file to the zip file using the given name/ 20 | // Create returns a io.Writer to which the file contents should be written. 21 | wr, err := zw.Create(filename) 22 | if err != nil { 23 | return err 24 | } 25 | // Write the file contents to the zip file. 26 | if _, err := io.Copy(wr, file); err != nil { 27 | return err 28 | } 29 | return nil 30 | } 31 | 32 | // archiveFiles archives a group of given files into a zip file. 33 | func archiveFiles(files []string, archive string) error { 34 | flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC 35 | // Open the tar file 36 | file, err := os.OpenFile(archive, flags, 0644) 37 | if err != nil { 38 | return err 39 | } 40 | defer file.Close() 41 | // Create zip.Writer that implements a zip file writer. 42 | zw := zip.NewWriter(file) 43 | defer zw.Close() 44 | // Iterate through the files to write each file into the zip file. 45 | for _, filename := range files { 46 | // Write the file into tar file. 47 | if err := addToArchive(filename, zw); err != nil { 48 | return err 49 | } 50 | } 51 | return nil 52 | } 53 | 54 | // readArchive reads the file contents from tar file. 55 | func readArchive(archive string) error { 56 | // Open the zip file specified by name and return a ReadCloser. 57 | rc, err := zip.OpenReader(archive) 58 | if err != nil { 59 | return err 60 | } 61 | defer rc.Close() 62 | // Iterate through the files in the zip file to read the file contents. 63 | for _, file := range rc.File { 64 | frc, err := file.Open() 65 | if err != nil { 66 | return err 67 | } 68 | defer frc.Close() 69 | fmt.Fprintf(os.Stdout, "Contents of the file %s:\n", file.Name) 70 | // Write the contents into Stdout 71 | copied, err := io.Copy(os.Stdout, frc) 72 | if err != nil { 73 | return err 74 | } 75 | // Check the size of the file. 76 | if uint64(copied) != file.UncompressedSize64 { 77 | return fmt.Errorf("Length of the file contents doesn't match with the file %s", file.Name) 78 | } 79 | fmt.Println() 80 | } 81 | return nil 82 | } 83 | 84 | func main() { 85 | // Name of the zip file 86 | archive := "source.zip" 87 | // Files to be archived in zip format. 88 | files := []string{"main.go", "readme.txt"} 89 | // Archive files into zip format. 90 | err := archiveFiles(files, archive) 91 | if err != nil { 92 | log.Fatalf("Error while writing to zip file:%s\n", err) 93 | } 94 | // Read the file contents of tar file. 95 | err = readArchive(archive) 96 | if err != nil { 97 | log.Fatalf("Error while reading the zip file:%s\n", err) 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /ch05/archivezip/readme.txt: -------------------------------------------------------------------------------- 1 | Example program using archive/zip package -------------------------------------------------------------------------------- /ch05/archivezip/source.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shijuvar/go-recipes/1ae7566656200ddab6d66ad478e9feb810690005/ch05/archivezip/source.zip -------------------------------------------------------------------------------- /ch05/cmdflags/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | 10 | fileName := flag.String("filename", "logfile", "File name for the log file") 11 | logLevel := flag.Int("loglevel", 0, "An integer value for Level (0-4)") 12 | isEnable := flag.Bool("enable", false, "A boolean value for enabling log options") 13 | var num int 14 | // Bind the flag to a variable. 15 | flag.IntVar(&num, "num", 25, "An integer value") 16 | 17 | // Parse parses flag definitions from the argument list. 18 | flag.Parse() 19 | // Get the values from pointers 20 | fmt.Println("filename:", *fileName) 21 | fmt.Println("loglevel:", *logLevel) 22 | fmt.Println("enable:", *isEnable) 23 | // Get the value from a variable 24 | fmt.Println("num:", num) 25 | // Args returns the non-flag command-line arguments. 26 | args := flag.Args() 27 | if len(args) > 0 { 28 | fmt.Println("The non-flag command-line arguments are:") 29 | // Print the arguments 30 | for _, v := range args { 31 | fmt.Println(v) 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /ch05/flag/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | 10 | strPtr := flag.String("name", "Shiju", "a string") 11 | numbPtr := flag.Int("num", 25, "an int") 12 | boolPtr := flag.Bool("enable", false, "a bool") 13 | var num int 14 | flag.IntVar(&num, "num", 30, "an int") 15 | // Parse parses flag definitions from the argument list. 16 | flag.Parse() 17 | // Get the values for pointers 18 | fmt.Println("name:", *strPtr) 19 | fmt.Println("num:", *numbPtr) 20 | fmt.Println("enable:", *boolPtr) 21 | // Get the value from a variable 22 | fmt.Println("num:", num) 23 | // Args returns the non-flag command-line arguments. 24 | fmt.Println("arguments:", flag.Args()) 25 | } 26 | -------------------------------------------------------------------------------- /ch05/json/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // Employee struct 9 | type Employee struct { 10 | ID int 11 | FirstName, LastName, JobTitle string 12 | } 13 | 14 | func main() { 15 | emp := Employee{ 16 | ID: 100, 17 | FirstName: "Shiju", 18 | LastName: "Varghese", 19 | JobTitle: "Architect", 20 | } 21 | // Encoding to JSON 22 | data, err := json.Marshal(emp) 23 | if err != nil { 24 | fmt.Println(err.Error()) 25 | return 26 | } 27 | jsonStr := string(data) 28 | fmt.Println("The JSON data is:") 29 | fmt.Println(jsonStr) 30 | 31 | b := []byte(`{"ID":101,"FirstName":"Irene","LastName":"Rose","JobTitle":"Developer"}`) 32 | var emp1 Employee 33 | // Decoding JSON data to a value of struct type 34 | err = json.Unmarshal(b, &emp1) 35 | if err != nil { 36 | fmt.Println(err.Error()) 37 | return 38 | } 39 | fmt.Println("The Employee value is:") 40 | fmt.Printf("ID:%d, Name:%s %s, JobTitle:%s", emp1.ID, emp1.FirstName, emp1.LastName, emp1.JobTitle) 41 | } 42 | -------------------------------------------------------------------------------- /ch05/jsontag/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // Employee struct with struct tags 9 | type Employee struct { 10 | ID int `json:"id,omitempty"` 11 | FirstName string `json:"firstname"` 12 | LastName string `json:"lastname"` 13 | JobTitle string `json:"job"` 14 | } 15 | 16 | func main() { 17 | emp := Employee{ 18 | FirstName: "Shiju", 19 | LastName: "Varghese", 20 | JobTitle: "Architect", 21 | } 22 | // Encoding to JSON 23 | data, err := json.Marshal(emp) 24 | if err != nil { 25 | fmt.Println(err.Error()) 26 | return 27 | } 28 | jsonStr := string(data) 29 | fmt.Println("The JSON data is:") 30 | fmt.Println(jsonStr) 31 | 32 | b := []byte(`{"id":101,"firstname":"Irene","lastname":"Rose","job":"Developer"}`) 33 | var emp1 Employee 34 | // Decoding JSON to a struct type 35 | err = json.Unmarshal(b, &emp1) 36 | if err != nil { 37 | fmt.Println(err.Error()) 38 | return 39 | } 40 | fmt.Println("The Employee value is:") 41 | fmt.Printf("ID:%d, Name:%s %s, JobTitle:%s", emp1.ID, emp1.FirstName, emp1.LastName, emp1.JobTitle) 42 | } 43 | -------------------------------------------------------------------------------- /ch05/log/logger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | ) 9 | 10 | const ( 11 | // UNSPECIFIED logs nothing 12 | UNSPECIFIED Level = iota // 0 : 13 | // TRACE logs everything 14 | TRACE // 1 15 | // INFO logs Info, Warnings and Errors 16 | INFO // 2 17 | // WARNING logs Warning and Errors 18 | WARNING // 3 19 | // ERROR just logs Errors 20 | ERROR // 4 21 | ) 22 | 23 | // Level holds the log level. 24 | type Level int 25 | 26 | // Package level variables, which are pointer to log.Logger. 27 | var ( 28 | Trace *log.Logger 29 | Info *log.Logger 30 | Warning *log.Logger 31 | Error *log.Logger 32 | ) 33 | 34 | // initLog initializes log.Logger objects 35 | func initLog( 36 | traceHandle io.Writer, 37 | infoHandle io.Writer, 38 | warningHandle io.Writer, 39 | errorHandle io.Writer, 40 | isFlag bool) { 41 | 42 | // Flags for defines the logging properties, to log.New 43 | flag := 0 44 | if isFlag { 45 | flag = log.Ldate | log.Ltime | log.Lshortfile 46 | } 47 | 48 | // Create log.Logger objects. 49 | Trace = log.New(traceHandle, "TRACE: ", flag) 50 | Info = log.New(infoHandle, "INFO: ", flag) 51 | Warning = log.New(warningHandle, "WARNING: ", flag) 52 | Error = log.New(errorHandle, "ERROR: ", flag) 53 | 54 | } 55 | 56 | // SetLogLevel sets the logging level preference 57 | func SetLogLevel(level Level) { 58 | 59 | // Creates os.*File, which has implemented io.Writer intreface 60 | f, err := os.OpenFile("logs.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 61 | if err != nil { 62 | log.Fatalf("Error opening log file: %s", err.Error()) 63 | } 64 | 65 | // Calls function initLog by specifying log level preference. 66 | switch level { 67 | case TRACE: 68 | initLog(f, f, f, f, true) 69 | return 70 | 71 | case INFO: 72 | initLog(ioutil.Discard, f, f, f, true) 73 | return 74 | 75 | case WARNING: 76 | initLog(ioutil.Discard, ioutil.Discard, f, f, true) 77 | return 78 | case ERROR: 79 | initLog(ioutil.Discard, ioutil.Discard, ioutil.Discard, f, true) 80 | return 81 | 82 | default: 83 | initLog(ioutil.Discard, ioutil.Discard, ioutil.Discard, ioutil.Discard, false) 84 | f.Close() 85 | return 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /ch05/log/logs.txt: -------------------------------------------------------------------------------- 1 | TRACE: 2016/06/13 22:04:28 main.go:14: Main started 2 | TRACE: 2016/06/13 22:04:28 main.go:23: Loop started 3 | INFO: 2016/06/13 22:04:28 main.go:25: Counter value is: 0 4 | INFO: 2016/06/13 22:04:28 main.go:25: Counter value is: 1 5 | INFO: 2016/06/13 22:04:28 main.go:25: Counter value is: 2 6 | INFO: 2016/06/13 22:04:28 main.go:25: Counter value is: 3 7 | INFO: 2016/06/13 22:04:28 main.go:25: Counter value is: 4 8 | INFO: 2016/06/13 22:04:28 main.go:25: Counter value is: 5 9 | INFO: 2016/06/13 22:04:28 main.go:25: Counter value is: 6 10 | INFO: 2016/06/13 22:04:28 main.go:25: Counter value is: 7 11 | INFO: 2016/06/13 22:04:28 main.go:25: Counter value is: 8 12 | INFO: 2016/06/13 22:04:28 main.go:25: Counter value is: 9 13 | WARNING: 2016/06/13 22:04:28 main.go:27: The counter variable is not being used 14 | TRACE: 2016/06/13 22:04:28 main.go:28: Loop completed 15 | ERROR: 2016/06/13 22:04:28 main.go:17: Sample Error 16 | TRACE: 2016/06/13 22:04:28 main.go:18: Main completed 17 | INFO: 2016/06/13 22:13:25 main.go:25: Counter value is: 0 18 | INFO: 2016/06/13 22:13:25 main.go:25: Counter value is: 1 19 | INFO: 2016/06/13 22:13:25 main.go:25: Counter value is: 2 20 | INFO: 2016/06/13 22:13:25 main.go:25: Counter value is: 3 21 | INFO: 2016/06/13 22:13:25 main.go:25: Counter value is: 4 22 | INFO: 2016/06/13 22:13:25 main.go:25: Counter value is: 5 23 | INFO: 2016/06/13 22:13:25 main.go:25: Counter value is: 6 24 | INFO: 2016/06/13 22:13:25 main.go:25: Counter value is: 7 25 | INFO: 2016/06/13 22:13:25 main.go:25: Counter value is: 8 26 | INFO: 2016/06/13 22:13:25 main.go:25: Counter value is: 9 27 | WARNING: 2016/06/13 22:13:25 main.go:27: The counter variable is not being used 28 | ERROR: 2016/06/13 22:13:25 main.go:17: Sample Error 29 | -------------------------------------------------------------------------------- /ch05/log/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | ) 7 | 8 | func main() { 9 | // Parse log level from command line 10 | logLevel := flag.Int("loglevel", 0, "an integer value (0-4)") 11 | flag.Parse() 12 | // Calling the SetLogLevel with the command-line argument 13 | SetLogLevel(Level(*logLevel)) 14 | Trace.Println("Main started") 15 | loop() 16 | err := errors.New("Sample Error") 17 | Error.Println(err.Error()) 18 | Trace.Println("Main completed") 19 | } 20 | 21 | // A simple function for the logging demo 22 | func loop() { 23 | Trace.Println("Loop started") 24 | for i := 0; i < 10; i++ { 25 | Info.Println("Counter value is:", i) 26 | } 27 | Warning.Println("The counter variable is not being used") 28 | Trace.Println("Loop completed") 29 | } 30 | -------------------------------------------------------------------------------- /ch05/simplelog/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | ) 10 | 11 | // Package level variables, which are pointer to log.Logger. 12 | var ( 13 | Trace *log.Logger 14 | Info *log.Logger 15 | Warning *log.Logger 16 | Error *log.Logger 17 | ) 18 | 19 | // initLog initializes log.Logger objects 20 | func initLog( 21 | traceHandle io.Writer, 22 | infoHandle io.Writer, 23 | warningHandle io.Writer, 24 | errorHandle io.Writer) { 25 | 26 | // Flags for defines the logging properties, to log.New 27 | flag := log.Ldate | log.Ltime | log.Lshortfile 28 | 29 | // Create log.Logger objects 30 | Trace = log.New(traceHandle, "TRACE: ", flag) 31 | Info = log.New(infoHandle, "INFO: ", flag) 32 | Warning = log.New(warningHandle, "WARNING: ", flag) 33 | Error = log.New(errorHandle, "ERROR: ", flag) 34 | 35 | } 36 | 37 | func main() { 38 | initLog(ioutil.Discard, os.Stdout, os.Stdout, os.Stderr) 39 | Trace.Println("Main started") 40 | loop() 41 | err := errors.New("Sample Error") 42 | Error.Println(err.Error()) 43 | Trace.Println("Main completed") 44 | } 45 | func loop() { 46 | Trace.Println("Loop started") 47 | for i := 0; i < 10; i++ { 48 | Info.Println("Counter value is:", i) 49 | } 50 | Warning.Println("The counter variable is not being used") 51 | Trace.Println("Loop completed") 52 | } 53 | -------------------------------------------------------------------------------- /ch06/influx/main.go: -------------------------------------------------------------------------------- 1 | // Example demo for working with InfluxDB 2 | package main 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "math/rand" 9 | "time" 10 | 11 | client "github.com/influxdata/influxdb/client/v2" 12 | ) 13 | 14 | const ( 15 | // DB provides the database name of the InfluxDB 16 | DB = "metricsdb" 17 | username = "opsadmin" 18 | password = "pass123" 19 | ) 20 | 21 | func main() { 22 | // Create client 23 | c := influxDBClient() 24 | // Write operations 25 | // Create metrics data for measurement "cpu" 26 | createMetrics(c) 27 | // Read operations 28 | // Read with limit of 10 29 | readWithLimit(c, 10) 30 | // Read mean value of "cpu_usage" for a region 31 | meanCPUUsage(c, "us-west") 32 | // Read count of records for a region 33 | countRegion(c, "us-west") 34 | 35 | } 36 | 37 | // influxDBClient returns InfluxDB Client 38 | func influxDBClient() client.Client { 39 | c, err := client.NewHTTPClient(client.HTTPConfig{ 40 | Addr: "http://localhost:8086", 41 | Username: username, 42 | Password: password, 43 | }) 44 | if err != nil { 45 | log.Fatalln("Error: ", err) 46 | } 47 | return c 48 | } 49 | 50 | // createMetrics write batch points to create the metrics data 51 | func createMetrics(clnt client.Client) { 52 | batchCount := 100 53 | rand.Seed(42) 54 | 55 | // Create BatchPoints by giving config for InfluxDB 56 | bp, _ := client.NewBatchPoints(client.BatchPointsConfig{ 57 | Database: DB, 58 | Precision: "s", 59 | }) 60 | // Batch update to adds Points 61 | for i := 0; i < batchCount; i++ { 62 | regions := []string{"us-west", "us-central", "us-north", "us-east"} 63 | // tagset – “host” and “region” 64 | tags := map[string]string{ 65 | "host": fmt.Sprintf("192.168.%d.%d", rand.Intn(100), rand.Intn(100)), 66 | "region": regions[rand.Intn(len(regions))], 67 | } 68 | 69 | value := rand.Float64() * 100.0 70 | // field - "cpu_usage" 71 | fields := map[string]interface{}{ 72 | "cpu_usage": value, 73 | } 74 | 75 | pt, err := client.NewPoint("cpu", tags, fields, time.Now()) 76 | 77 | if err != nil { 78 | log.Fatalln("Error: ", err) 79 | } 80 | // Add a Point 81 | bp.AddPoint(pt) 82 | 83 | } 84 | // Writes the batch update to add points to measurement "cpu" 85 | err := clnt.Write(bp) 86 | if err != nil { 87 | log.Fatalln("Error: ", err) 88 | } 89 | } 90 | 91 | // queryDB query the database 92 | func queryDB(clnt client.Client, command string) (res []client.Result, err error) { 93 | // Create the query 94 | q := client.Query{ 95 | Command: command, 96 | Database: DB, 97 | } 98 | // Query the Database 99 | if response, err := clnt.Query(q); err == nil { 100 | if response.Error() != nil { 101 | return res, response.Error() 102 | } 103 | res = response.Results 104 | } else { 105 | return res, err 106 | } 107 | return res, nil 108 | } 109 | 110 | // readWithLimit reads records with a given limit 111 | func readWithLimit(clnt client.Client, limit int) { 112 | q := fmt.Sprintf("SELECT * FROM %s LIMIT %d", "cpu", limit) 113 | res, err := queryDB(clnt, q) 114 | if err != nil { 115 | log.Fatalln("Error: ", err) 116 | } 117 | 118 | for i, row := range res[0].Series[0].Values { 119 | t, err := time.Parse(time.RFC3339, row[0].(string)) 120 | if err != nil { 121 | log.Fatalln("Error: ", err) 122 | } 123 | val, err := row[1].(json.Number).Float64() 124 | fmt.Printf("[%2d] %s: %f\n", i, t.Format(time.Stamp), val) 125 | } 126 | } 127 | 128 | // meanCPUUsage reads the mean value of cpu_usage 129 | func meanCPUUsage(clnt client.Client, region string) { 130 | q := fmt.Sprintf("select mean(%s) from %s where region = '%s'", "cpu_usage", "cpu", region) 131 | res, err := queryDB(clnt, q) 132 | if err != nil { 133 | log.Fatalln("Error: ", err) 134 | } 135 | value, err := res[0].Series[0].Values[0][1].(json.Number).Float64() 136 | if err != nil { 137 | log.Fatalln("Error: ", err) 138 | } 139 | 140 | fmt.Printf("Mean value of cpu_usage for region '%s':%f\n", region, value) 141 | } 142 | 143 | // countRegion reads the count of records for a given region 144 | func countRegion(clnt client.Client, region string) { 145 | q := fmt.Sprintf("SELECT count(%s) FROM %s where region = '%s'", "cpu_usage", "cpu", region) 146 | res, err := queryDB(clnt, q) 147 | if err != nil { 148 | log.Fatalln("Error: ", err) 149 | } 150 | count := res[0].Series[0].Values[0][1] 151 | fmt.Printf("Found a total of %v records for region '%s'\n", count, region) 152 | } 153 | -------------------------------------------------------------------------------- /ch06/mongo/bookmark_store.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "gopkg.in/mgo.v2" 7 | "gopkg.in/mgo.v2/bson" 8 | ) 9 | 10 | // Bookmark type reperesents the metadata of a bookmark. 11 | type Bookmark struct { 12 | ID bson.ObjectId `bson:"_id,omitempty"` 13 | Name, Description, Location string 14 | Priority int // Priority (1 -5) 15 | CreatedOn time.Time 16 | Tags []string 17 | } 18 | 19 | // BookmarkStore provides CRUD operations against the collection "bookmarks". 20 | type BookmarkStore struct { 21 | C *mgo.Collection 22 | } 23 | 24 | // Create inserts the value of struct Bookmark into collection. 25 | func (store BookmarkStore) Create(b *Bookmark) error { 26 | // Assign a new bson.ObjectId 27 | b.ID = bson.NewObjectId() 28 | err := store.C.Insert(b) 29 | return err 30 | } 31 | 32 | //Update modifies an existing value of a collection. 33 | func (store BookmarkStore) Update(b Bookmark) error { 34 | // partial update on MogoDB 35 | err := store.C.Update(bson.M{"_id": b.ID}, 36 | bson.M{"$set": bson.M{ 37 | "name": b.Name, 38 | "description": b.Description, 39 | "location": b.Location, 40 | "priority": b.Priority, 41 | "tags": b.Tags, 42 | }}) 43 | return err 44 | } 45 | 46 | // Delete removes an existing value from the collection. 47 | func (store BookmarkStore) Delete(id string) error { 48 | err := store.C.Remove(bson.M{"_id": bson.ObjectIdHex(id)}) 49 | return err 50 | } 51 | 52 | // GetAll returns all documents from the collection. 53 | func (store BookmarkStore) GetAll() []Bookmark { 54 | var b []Bookmark 55 | iter := store.C.Find(nil).Sort("priority", "-createdon").Iter() 56 | result := Bookmark{} 57 | for iter.Next(&result) { 58 | b = append(b, result) 59 | } 60 | return b 61 | } 62 | 63 | // GetByID returns single document from the collection. 64 | func (store BookmarkStore) GetByID(id string) (Bookmark, error) { 65 | var b Bookmark 66 | err := store.C.FindId(bson.ObjectIdHex(id)).One(&b) 67 | return b, err 68 | } 69 | 70 | // GetByTag returns all documents from the collection filtering by tags. 71 | func (store BookmarkStore) GetByTag(tags []string) []Bookmark { 72 | var b []Bookmark 73 | iter := store.C.Find(bson.M{"tags": bson.M{"$in": tags}}).Sort("priority", "-createdon").Iter() 74 | result := Bookmark{} 75 | for iter.Next(&result) { 76 | b = append(b, result) 77 | } 78 | return b 79 | } 80 | -------------------------------------------------------------------------------- /ch06/mongo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "gopkg.in/mgo.v2" 9 | ) 10 | 11 | var store BookmarkStore 12 | var id string 13 | 14 | // init will invoke before the function main. 15 | func init() { 16 | session, err := mgo.DialWithInfo(&mgo.DialInfo{ 17 | Addrs: []string{"127.0.0.1"}, 18 | Timeout: 60 * time.Second, 19 | }) 20 | if err != nil { 21 | log.Fatalf("[MongoDB Session]: %s\n", err) 22 | } 23 | collection := session.DB("bookmarkdb").C("bookmarks") 24 | collection.RemoveAll(nil) 25 | store = BookmarkStore{ 26 | C: collection, 27 | } 28 | } 29 | 30 | // Create and update documents. 31 | func createUpdate() { 32 | bookmark := Bookmark{ 33 | Name: "mgo", 34 | Description: "Go driver for MongoDB", 35 | Location: "https://github.com/go-mgo/mgo", 36 | Priority: 2, 37 | CreatedOn: time.Now(), 38 | Tags: []string{"go", "nosql", "mongodb"}, 39 | } 40 | // Insert a new document. 41 | if err := store.Create(&bookmark); err != nil { 42 | log.Fatalf("[Create]: %s\n", err) 43 | } 44 | id = bookmark.ID.Hex() 45 | fmt.Printf("New bookmark has been inserted with ID: %s\n", id) 46 | // Update an existing document. 47 | bookmark.Priority = 1 48 | if err := store.Update(bookmark); err != nil { 49 | log.Fatalf("[Update]: %s\n", err) 50 | } 51 | fmt.Println("The value after update:") 52 | // Retrieve the updated document. 53 | getByID(id) 54 | 55 | bookmark = Bookmark{ 56 | Name: "gorethink", 57 | Description: "Go driver for RethinkDB", 58 | Location: "https://github.com/dancannon/gorethink", 59 | Priority: 3, 60 | CreatedOn: time.Now(), 61 | Tags: []string{"go", "nosql", "rethinkdb"}, 62 | } 63 | // Insert a new document. 64 | if err := store.Create(&bookmark); err != nil { 65 | log.Fatalf("[Create]: %s\n", err) 66 | } 67 | id = bookmark.ID.Hex() 68 | fmt.Printf("New bookmark has been inserted with ID: %s\n", id) 69 | 70 | } 71 | 72 | // Get a document by given id. 73 | func getByID(id string) { 74 | bookmark, err := store.GetByID(id) 75 | if err != nil { 76 | log.Fatalf("[GetByID]: %s\n", err) 77 | } 78 | fmt.Printf("Name:%s, Description:%s, Priority:%d\n", 79 | bookmark.Name, bookmark.Description, bookmark.Priority) 80 | } 81 | 82 | // Get all documents from the collection. 83 | func getAll() { 84 | // Layout for formatting dates. 85 | layout := "2006-01-02 15:04:05" 86 | // Retrieve all documents. 87 | bookmarks := store.GetAll() 88 | fmt.Println("Read all documents") 89 | for _, v := range bookmarks { 90 | fmt.Printf("Name:%s, Description:%s, Priority:%d, CreatedOn:%s\n", 91 | v.Name, v.Description, v.Priority, v.CreatedOn.Format(layout)) 92 | } 93 | } 94 | 95 | // Get documents by tags. 96 | func getByTags() { 97 | layout := "2006-01-02 15:04:05" 98 | fmt.Println("Query with Tags - 'go, nosql'") 99 | bookmarks := store.GetByTag([]string{"go", "nosql"}) 100 | for _, v := range bookmarks { 101 | fmt.Printf("Name:%s, Description:%s, Priority:%d, CreatedOn:%s\n", 102 | v.Name, v.Description, v.Priority, v.CreatedOn.Format(layout)) 103 | } 104 | fmt.Println("Query with Tags - 'mongodb'") 105 | bookmarks = store.GetByTag([]string{"mongodb"}) 106 | for _, v := range bookmarks { 107 | fmt.Printf("Name:%s, Description:%s, Priority:%d, CreatedOn:%s\n", 108 | v.Name, v.Description, v.Priority, v.CreatedOn.Format(layout)) 109 | } 110 | } 111 | 112 | // Delete an existing document from the collection. 113 | func delete() { 114 | if err := store.Delete(id); err != nil { 115 | log.Fatalf("[Delete]: %s\n", err) 116 | } 117 | bookmarks := store.GetAll() 118 | fmt.Printf("Number of documents in the collection after delete:%d\n", len(bookmarks)) 119 | } 120 | 121 | // main - entry point of the program. 122 | func main() { 123 | createUpdate() 124 | getAll() 125 | getByTags() 126 | delete() 127 | } 128 | -------------------------------------------------------------------------------- /ch06/postgres/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | 8 | _ "github.com/lib/pq" 9 | ) 10 | 11 | // Product struct provides the data model for productstore 12 | type Product struct { 13 | ID int 14 | Title string 15 | Description string 16 | Price float32 17 | } 18 | 19 | var db *sql.DB 20 | 21 | func init() { 22 | var err error 23 | db, err = sql.Open("postgres", "postgres://user:pass@localhost/productstore") 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | } 28 | func main() { 29 | product := Product{ 30 | Title: "Amazon Echo", 31 | Description: "Amazon Echo - Black", 32 | Price: 179.99, 33 | } 34 | // Insert a product 35 | createProduct(product) 36 | // Read all product records 37 | getProducts() 38 | // Read a product by given id 39 | getProductByID(1) 40 | } 41 | 42 | // createProduct inserts product values into product table 43 | func createProduct(prd Product) { 44 | result, err := db.Exec("INSERT INTO products(title, description, price) VALUES($1, $2, $3)", prd.Title, prd.Description, prd.Price) 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | 49 | lastInsertID, err := result.LastInsertId() 50 | rowsAffected, err := result.RowsAffected() 51 | fmt.Printf("Product with id=%d created successfully (%d row affected)\n", lastInsertID, rowsAffected) 52 | } 53 | 54 | // getProducts reads all records from the product table 55 | func getProducts() { 56 | rows, err := db.Query("SELECT * FROM products") 57 | if err != nil { 58 | if err == sql.ErrNoRows { 59 | fmt.Println("No Records Found") 60 | return 61 | } 62 | log.Fatal(err) 63 | } 64 | defer rows.Close() 65 | 66 | var products []*Product 67 | for rows.Next() { 68 | prd := &Product{} 69 | err := rows.Scan(&prd.Title, &prd.Description, &prd.Price) 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | products = append(products, prd) 74 | } 75 | if err = rows.Err(); err != nil { 76 | log.Fatal(err) 77 | } 78 | 79 | for _, pr := range products { 80 | fmt.Printf("%s, %s, $%.2f\n", pr.Title, pr.Description, pr.Price) 81 | } 82 | } 83 | 84 | func getProductByID(id int) { 85 | var product string 86 | err := db.QueryRow("SELECT title FROM products WHERE id=$1", id).Scan(&product) 87 | switch { 88 | case err == sql.ErrNoRows: 89 | log.Printf("No product with that ID.") 90 | case err != nil: 91 | log.Fatal(err) 92 | default: 93 | fmt.Printf("Product is %s\n", product) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /ch06/rethink/bookmark_store.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | r "github.com/dancannon/gorethink" 7 | ) 8 | 9 | // Bookmark type reperesents the metadata of a bookmark. 10 | type Bookmark struct { 11 | ID string `gorethink:"id,omitempty" json:"id"` 12 | Name, Description, Location string 13 | Priority int // Priority (1 -5) 14 | CreatedOn time.Time 15 | Tags []string 16 | } 17 | 18 | // BookmarkStore provides CRUD operations against the Table "bookmarks". 19 | type BookmarkStore struct { 20 | Session *r.Session 21 | } 22 | 23 | // Create inserts the value of struct Bookmark into Table. 24 | func (store BookmarkStore) Create(b *Bookmark) error { 25 | 26 | resp, err := r.Table("bookmarks").Insert(b).RunWrite(store.Session) 27 | if err == nil { 28 | b.ID = resp.GeneratedKeys[0] 29 | } 30 | 31 | return err 32 | } 33 | 34 | // Update modifies an existing value of a Table. 35 | func (store BookmarkStore) Update(b Bookmark) error { 36 | 37 | var data = map[string]interface{}{ 38 | "description": b.Description, 39 | "location": b.Location, 40 | "priority": b.Priority, 41 | "tags": b.Tags, 42 | } 43 | // partial update on RethinkDB 44 | _, err := r.Table("bookmarks").Get(b.ID).Update(data).RunWrite(store.Session) 45 | return err 46 | } 47 | 48 | // Delete removes an existing value from the Table. 49 | func (store BookmarkStore) Delete(id string) error { 50 | _, err := r.Table("bookmarks").Get(id).Delete().RunWrite(store.Session) 51 | return err 52 | } 53 | 54 | // GetAll returns all documents from the Table. 55 | func (store BookmarkStore) GetAll() ([]Bookmark, error) { 56 | bookmarks := []Bookmark{} 57 | 58 | res, err := r.Table("bookmarks").OrderBy("priority", r.Desc("createdon")).Run(store.Session) 59 | err = res.All(&bookmarks) 60 | return bookmarks, err 61 | } 62 | 63 | // GetByID returns single document from the Table. 64 | func (store BookmarkStore) GetByID(id string) (Bookmark, error) { 65 | var b Bookmark 66 | res, err := r.Table("bookmarks").Get(id).Run(store.Session) 67 | res.One(&b) 68 | return b, err 69 | } 70 | 71 | // // GetByTag returns all documents from the collection filtering by tags. 72 | // func (store BookmarkStore) GetByTag(tags []string) ([]Bookmark, error) { 73 | // bookmarks := []Bookmark{} 74 | // res, err := r.Table("bookmarks").Filter(func(row r.Term) r.Term { 75 | // return r.Expr(tags).Contains(row.Field("tags")) 76 | // }).Run(store.Session) 77 | // err = res.All(&bookmarks) 78 | // return bookmarks, err 79 | // } 80 | -------------------------------------------------------------------------------- /ch06/rethink/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | r "github.com/dancannon/gorethink" 9 | ) 10 | 11 | var store BookmarkStore 12 | var id string 13 | 14 | // initDB creates new database and 15 | func initDB(session *r.Session) { 16 | var err error 17 | // Create Database 18 | _, err = r.DBCreate("bookmarkdb").RunWrite(session) 19 | if err != nil { 20 | log.Fatalf("[initDB]: %s\n", err) 21 | } 22 | // Create Table 23 | _, err = r.DB("bookmarkdb").TableCreate("bookmarks").RunWrite(session) 24 | if err != nil { 25 | log.Fatalf("[initDB]: %s\n", err) 26 | } 27 | } 28 | 29 | // changeFeeds subscribes real-time updates on table bookmarks. 30 | func changeFeeds(session *r.Session) { 31 | bookmarks, err := r.Table("bookmarks").Changes().Field("new_val").Run(session) 32 | if err != nil { 33 | log.Fatalf("[changeFeeds]: %s\n", err) 34 | } 35 | // Luanch a goroutine to print real-time updates. 36 | go func() { 37 | var bookmark Bookmark 38 | for bookmarks.Next(&bookmark) { 39 | if bookmark.ID == "" { // for delete, new_val will be null. 40 | fmt.Println("Real-time update: Document has been deleted") 41 | } else { 42 | fmt.Printf("Real-time update: Name:%s, Description:%s, Priority:%d\n", 43 | bookmark.Name, bookmark.Description, bookmark.Priority) 44 | } 45 | } 46 | }() 47 | } 48 | 49 | // init will invoke before the function main. 50 | func init() { 51 | session, err := r.Connect(r.ConnectOpts{ 52 | Address: "localhost:28015", 53 | Database: "bookmarkdb", 54 | MaxIdle: 10, 55 | MaxOpen: 10, 56 | }) 57 | 58 | if err != nil { 59 | log.Fatalf("[RethinkDB Session]: %s\n", err) 60 | } 61 | r.Table("bookmarks").Delete().Run(session) 62 | // Create Database and Table. 63 | //initDB(session) 64 | store = BookmarkStore{ 65 | Session: session, 66 | } 67 | // Subscribe real-time changes 68 | changeFeeds(session) 69 | } 70 | 71 | // Create and update documents. 72 | func createUpdate() { 73 | bookmark := Bookmark{ 74 | Name: "mgo", 75 | Description: "Go driver for MongoDB", 76 | Location: "https://github.com/go-mgo/mgo", 77 | Priority: 1, 78 | CreatedOn: time.Now(), 79 | Tags: []string{"go", "nosql", "mongodb"}, 80 | } 81 | // Insert a new document. 82 | if err := store.Create(&bookmark); err != nil { 83 | log.Fatalf("[Create]: %s\n", err) 84 | } 85 | id = bookmark.ID 86 | fmt.Printf("New bookmark has been inserted with ID: %s\n", id) 87 | // Retrieve the updated document. 88 | bookmark.Priority = 2 89 | if err := store.Update(bookmark); err != nil { 90 | log.Fatalf("[Update]: %s\n", err) 91 | } 92 | fmt.Println("The value after update:") 93 | // Retrieve an existing document by id. 94 | getByID(id) 95 | bookmark = Bookmark{ 96 | Name: "gorethink", 97 | Description: "Go driver for RethinkDB", 98 | Location: "https://github.com/dancannon/gorethink", 99 | Priority: 1, 100 | CreatedOn: time.Now(), 101 | Tags: []string{"go", "nosql", "rethinkdb"}, 102 | } 103 | // Insert a new document. 104 | if err := store.Create(&bookmark); err != nil { 105 | log.Fatalf("[Create]: %s\n", err) 106 | } 107 | id = bookmark.ID 108 | fmt.Printf("New bookmark has been inserted with ID: %s\n", id) 109 | 110 | } 111 | 112 | // Get a document by given id. 113 | func getByID(id string) { 114 | bookmark, err := store.GetByID(id) 115 | if err != nil { 116 | log.Fatalf("[GetByID]: %s\n", err) 117 | } 118 | fmt.Printf("Name:%s, Description:%s, Priority:%d\n", bookmark.Name, bookmark.Description, bookmark.Priority) 119 | } 120 | 121 | // Get all documents from bookmarks table. 122 | func getAll() { 123 | // Layout for formatting dates. 124 | layout := "2006-01-02 15:04:05" 125 | // Retrieve all documents. 126 | bookmarks, err := store.GetAll() 127 | if err != nil { 128 | log.Fatalf("[GetAll]: %s\n", err) 129 | } 130 | fmt.Println("Read all documents") 131 | for _, v := range bookmarks { 132 | fmt.Printf("Name:%s, Description:%s, Priority:%d, CreatedOn:%s\n", v.Name, v.Description, v.Priority, v.CreatedOn.Format(layout)) 133 | } 134 | 135 | } 136 | 137 | // Delete an existing document from bookmarks table. 138 | func delete() { 139 | if err := store.Delete(id); err != nil { 140 | log.Fatalf("[Delete]: %s\n", err) 141 | } 142 | bookmarks, err := store.GetAll() 143 | if err != nil { 144 | log.Fatalf("[GetAll]: %s\n", err) 145 | } 146 | fmt.Printf("Number of documents in the table after delete:%d\n", len(bookmarks)) 147 | } 148 | 149 | // main - entry point of the program 150 | func main() { 151 | createUpdate() 152 | getAll() 153 | delete() 154 | } 155 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/common/auth.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "context" 5 | "crypto/rsa" 6 | "errors" 7 | "io/ioutil" 8 | "log" 9 | "net/http" 10 | "strings" 11 | "time" 12 | 13 | jwt "github.com/dgrijalva/jwt-go" 14 | "github.com/dgrijalva/jwt-go/request" 15 | ) 16 | 17 | // AppClaims provides custom claim for JWT 18 | type AppClaims struct { 19 | UserName string `json:"username"` 20 | Role string `json:"role"` 21 | jwt.StandardClaims 22 | } 23 | 24 | // using asymmetric crypto/RSA keys 25 | // location of private/public key files 26 | const ( 27 | // openssl genrsa -out app.rsa 1024 28 | privKeyPath = "keys/app.rsa" 29 | // openssl rsa -in app.rsa -pubout > app.rsa.pub 30 | pubKeyPath = "keys/app.rsa.pub" 31 | ) 32 | 33 | // Private key for signing and public key for verification 34 | var ( 35 | //verifyKey, signKey []byte 36 | verifyKey *rsa.PublicKey 37 | signKey *rsa.PrivateKey 38 | ) 39 | 40 | // Read the key files before starting http handlers 41 | func initKeys() { 42 | 43 | signBytes, err := ioutil.ReadFile(privKeyPath) 44 | if err != nil { 45 | log.Fatalf("[initKeys]: %s\n", err) 46 | } 47 | 48 | signKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes) 49 | if err != nil { 50 | log.Fatalf("[initKeys]: %s\n", err) 51 | } 52 | 53 | verifyBytes, err := ioutil.ReadFile(pubKeyPath) 54 | if err != nil { 55 | log.Fatalf("[initKeys]: %s\n", err) 56 | } 57 | 58 | verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes) 59 | if err != nil { 60 | log.Fatalf("[initKeys]: %s\n", err) 61 | } 62 | } 63 | 64 | // GenerateJWT generates a new JWT token for authenticated user. 65 | func GenerateJWT(name, role string) (string, error) { 66 | // Create the Claims 67 | claims := AppClaims{ 68 | UserName: name, 69 | Role: role, 70 | StandardClaims: jwt.StandardClaims{ 71 | ExpiresAt: time.Now().Add(time.Minute * 20).Unix(), 72 | Issuer: "admin", 73 | }, 74 | } 75 | token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) 76 | ss, err := token.SignedString(signKey) 77 | if err != nil { 78 | return "", err 79 | } 80 | return ss, nil 81 | } 82 | 83 | // AuthorizeRequest Middleware validates JWT tokens from incoming HTTP requests. 84 | func AuthorizeRequest(next http.Handler) http.Handler { 85 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 86 | // Get token from request 87 | token, err := request.ParseFromRequestWithClaims(r, request.OAuth2Extractor, &AppClaims{}, func(token *jwt.Token) (interface{}, error) { 88 | // since we only use the one private key to sign the tokens, 89 | // we also only use its public counter part to verify 90 | return verifyKey, nil 91 | }) 92 | 93 | if err != nil { 94 | switch err.(type) { 95 | 96 | case *jwt.ValidationError: // JWT validation error 97 | vErr := err.(*jwt.ValidationError) 98 | 99 | switch vErr.Errors { 100 | case jwt.ValidationErrorExpired: //JWT expired 101 | DisplayAppError( 102 | w, 103 | err, 104 | "Access Token is expired, get a new Token", 105 | 401, 106 | ) 107 | return 108 | 109 | default: 110 | DisplayAppError(w, 111 | err, 112 | "Error while parsing the Access Token!", 113 | 500, 114 | ) 115 | return 116 | } 117 | 118 | default: 119 | DisplayAppError(w, 120 | err, 121 | "Error while parsing Access Token!", 122 | 500) 123 | return 124 | } 125 | 126 | } 127 | if token.Valid { 128 | // Create a Context by setting the user name 129 | ctx := context.WithValue(r.Context(), "user", token.Claims.(*AppClaims).UserName) 130 | // Calls the next handler by providing the Context 131 | next.ServeHTTP(w, r.WithContext(ctx)) 132 | } else { 133 | DisplayAppError( 134 | w, 135 | err, 136 | "Invalid Access Token", 137 | 401, 138 | ) 139 | } 140 | }) 141 | } 142 | 143 | // AuthorizeRequestWithNegroni is a Negroni Middleware that validates JWT tokens 144 | func AuthorizeRequestWithNegroni(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 145 | 146 | // Get token from request 147 | token, err := request.ParseFromRequestWithClaims(r, request.OAuth2Extractor, &AppClaims{}, func(token *jwt.Token) (interface{}, error) { 148 | // since we only use the one private key to sign the tokens, 149 | // we also only use its public counter part to verify 150 | return verifyKey, nil 151 | }) 152 | 153 | if err != nil { 154 | switch err.(type) { 155 | 156 | case *jwt.ValidationError: // JWT validation error 157 | vErr := err.(*jwt.ValidationError) 158 | 159 | switch vErr.Errors { 160 | case jwt.ValidationErrorExpired: //JWT expired 161 | DisplayAppError( 162 | w, 163 | err, 164 | "Access Token is expired, get a new Token", 165 | 401, 166 | ) 167 | return 168 | 169 | default: 170 | DisplayAppError(w, 171 | err, 172 | "Error while parsing the Access Token!", 173 | 500, 174 | ) 175 | return 176 | } 177 | 178 | default: 179 | DisplayAppError(w, 180 | err, 181 | "Error while parsing Access Token!", 182 | 500) 183 | return 184 | } 185 | 186 | } 187 | if token.Valid { 188 | ctx := context.WithValue(r.Context(), "user", token.Claims.(*AppClaims).UserName) 189 | next(w, r.WithContext(ctx)) 190 | } else { 191 | DisplayAppError( 192 | w, 193 | err, 194 | "Invalid Access Token", 195 | 401, 196 | ) 197 | } 198 | } 199 | 200 | // TokenFromAuthHeader is a "TokenExtractor" that takes a given request and extracts 201 | // the JWT token from the Authorization header. 202 | func TokenFromAuthHeader(r *http.Request) (string, error) { 203 | // Look for an Authorization header 204 | if ah := r.Header.Get("Authorization"); ah != "" { 205 | // Should be a bearer token 206 | if len(ah) > 6 && strings.ToUpper(ah[0:6]) == "BEARER" { 207 | return ah[7:], nil 208 | } 209 | } 210 | return "", errors.New("No token in the HTTP request") 211 | } 212 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/common/bootstrapper.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | // StartUp bootstrapps the application 4 | func StartUp() { 5 | // Initialize AppConfig variable 6 | initConfig() 7 | // Initialize private/public keys for JWT authentication 8 | initKeys() 9 | // Initialize Logger objects with Log Level 10 | setLogLevel(Level(AppConfig.LogLevel)) 11 | // Start a MongoDB session 12 | createDBSession() 13 | // Add indexes into MongoDB 14 | addIndexes() 15 | } 16 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/common/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "Server" : "0.0.0.0:8080", 3 | "MongoDBHost" : "127.0.0.1", 4 | "MongoDBUser" : "", 5 | "MongoDBPwd" : "", 6 | "Database" : "bookmarkdb", 7 | "LogLevel" : 4 8 | } 9 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/common/logger.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | ) 9 | 10 | const ( 11 | // UNSPECIFIED logs nothing 12 | UNSPECIFIED Level = iota // 0 : 13 | // TRACE logs everything 14 | TRACE // 1 15 | // INFO logs Info, Warnings and Errors 16 | INFO // 2 17 | // WARNING logs Warning and Errors 18 | WARNING // 3 19 | // ERROR just logs Errors 20 | ERROR // 4 21 | ) 22 | 23 | // Level holds the log level. 24 | type Level int 25 | 26 | // Package level variables, which are pointer to log.Logger. 27 | var ( 28 | Trace *log.Logger 29 | Info *log.Logger 30 | Warning *log.Logger 31 | Error *log.Logger 32 | ) 33 | 34 | // initLog initializes log.Logger objects 35 | func initLog( 36 | traceHandle io.Writer, 37 | infoHandle io.Writer, 38 | warningHandle io.Writer, 39 | errorHandle io.Writer, 40 | isFlag bool) { 41 | 42 | // Flags for defines the logging properties, to log.New 43 | flag := 0 44 | if isFlag { 45 | flag = log.Ldate | log.Ltime | log.Lshortfile 46 | } 47 | 48 | // Create log.Logger objects. 49 | Trace = log.New(traceHandle, "TRACE: ", flag) 50 | Info = log.New(infoHandle, "INFO: ", flag) 51 | Warning = log.New(warningHandle, "WARNING: ", flag) 52 | Error = log.New(errorHandle, "ERROR: ", flag) 53 | 54 | } 55 | 56 | // SetLogLevel sets the logging level preference 57 | func setLogLevel(level Level) { 58 | 59 | // Creates os.*File, which has implemented io.Writer intreface 60 | f, err := os.OpenFile("logs.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 61 | if err != nil { 62 | log.Fatalf("Error opening log file: %s", err.Error()) 63 | } 64 | 65 | // Calls function initLog by specifying log level preference. 66 | switch level { 67 | case TRACE: 68 | initLog(f, f, f, f, true) 69 | return 70 | 71 | case INFO: 72 | initLog(ioutil.Discard, f, f, f, true) 73 | return 74 | 75 | case WARNING: 76 | initLog(ioutil.Discard, ioutil.Discard, f, f, true) 77 | return 78 | case ERROR: 79 | initLog(ioutil.Discard, ioutil.Discard, ioutil.Discard, f, true) 80 | return 81 | 82 | default: 83 | initLog(ioutil.Discard, ioutil.Discard, ioutil.Discard, ioutil.Discard, false) 84 | f.Close() 85 | return 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/common/mongo_utils.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "gopkg.in/mgo.v2" 8 | ) 9 | 10 | var session *mgo.Session 11 | 12 | // GetSession returns a MongoDB Session 13 | func getSession() *mgo.Session { 14 | if session == nil { 15 | var err error 16 | session, err = mgo.DialWithInfo(&mgo.DialInfo{ 17 | Addrs: []string{AppConfig.MongoDBHost}, 18 | Username: AppConfig.DBUser, 19 | Password: AppConfig.DBPwd, 20 | Timeout: 60 * time.Second, 21 | }) 22 | if err != nil { 23 | log.Fatalf("[GetSession]: %s\n", err) 24 | } 25 | } 26 | return session 27 | } 28 | func createDBSession() { 29 | var err error 30 | session, err = mgo.DialWithInfo(&mgo.DialInfo{ 31 | Addrs: []string{AppConfig.MongoDBHost}, 32 | Username: AppConfig.DBUser, 33 | Password: AppConfig.DBPwd, 34 | Timeout: 60 * time.Second, 35 | }) 36 | if err != nil { 37 | log.Fatalf("[createDbSession]: %s\n", err) 38 | } 39 | } 40 | 41 | // Add indexes into MongoDB 42 | func addIndexes() { 43 | var err error 44 | userIndex := mgo.Index{ 45 | Key: []string{"email"}, 46 | Unique: true, 47 | Background: true, 48 | Sparse: true, 49 | } 50 | // Add indexes into MongoDB 51 | session := getSession().Copy() 52 | defer session.Close() 53 | userCol := session.DB(AppConfig.Database).C("users") 54 | 55 | err = userCol.EnsureIndex(userIndex) 56 | if err != nil { 57 | log.Fatalf("[addIndexes]: %s\n", err) 58 | } 59 | } 60 | 61 | // DataStore for MongoDB 62 | type DataStore struct { 63 | MongoSession *mgo.Session 64 | } 65 | 66 | // Close closes a mgo.Session value. 67 | // Used to add defer statements for closing the copied session. 68 | func (ds *DataStore) Close() { 69 | ds.MongoSession.Close() 70 | } 71 | 72 | // Collection returns mgo.collection for the given name 73 | func (ds *DataStore) Collection(name string) *mgo.Collection { 74 | return ds.MongoSession.DB(AppConfig.Database).C(name) 75 | } 76 | 77 | // NewDataStore creates a new DataStore object to be used for each HTTP request. 78 | func NewDataStore() *DataStore { 79 | session := getSession().Copy() 80 | dataStore := &DataStore{ 81 | MongoSession: session, 82 | } 83 | return dataStore 84 | } 85 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/common/utils.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | type ( 11 | appError struct { 12 | Error string `json:"error"` 13 | Message string `json:"message"` 14 | HTTPStatus int `json:"status"` 15 | } 16 | errorResource struct { 17 | Data appError `json:"data"` 18 | } 19 | configuration struct { 20 | Server, MongoDBHost, DBUser, DBPwd, Database string 21 | LogLevel int 22 | } 23 | ) 24 | 25 | // DisplayAppError provides app specific error in JSON 26 | func DisplayAppError(w http.ResponseWriter, handlerError error, message string, code int) { 27 | errObj := appError{ 28 | Error: handlerError.Error(), 29 | Message: message, 30 | HTTPStatus: code, 31 | } 32 | //log.Printf("AppError]: %s\n", handlerError) 33 | Error.Printf("AppError]: %s\n", handlerError) 34 | w.Header().Set("Content-Type", "application/json; charset=utf-8") 35 | w.WriteHeader(code) 36 | if j, err := json.Marshal(errorResource{Data: errObj}); err == nil { 37 | w.Write(j) 38 | } 39 | } 40 | 41 | // AppConfig holds the configuration values from config.json file 42 | var AppConfig configuration 43 | 44 | // Initialize AppConfig 45 | func initConfig() { 46 | loadAppConfig() 47 | } 48 | 49 | // Reads config.json and decode into AppConfig 50 | func loadAppConfig() { 51 | file, err := os.Open("common/config.json") 52 | defer file.Close() 53 | if err != nil { 54 | log.Fatalf("[loadConfig]: %s\n", err) 55 | } 56 | decoder := json.NewDecoder(file) 57 | AppConfig = configuration{} 58 | err = decoder.Decode(&AppConfig) 59 | if err != nil { 60 | log.Fatalf("[loadAppConfig]: %s\n", err) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/controllers/bookmark_controller.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/gorilla/mux" 8 | "gopkg.in/mgo.v2" 9 | "gopkg.in/mgo.v2/bson" 10 | 11 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/common" 12 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/store" 13 | ) 14 | 15 | // CreateBookmark insert a new Bookmark. 16 | // Handler for HTTP Post - "/bookmarks 17 | func CreateBookmark(w http.ResponseWriter, r *http.Request) { 18 | var dataResource BookmarkResource 19 | // Decode the incoming Bookmark json 20 | err := json.NewDecoder(r.Body).Decode(&dataResource) 21 | if err != nil { 22 | common.DisplayAppError( 23 | w, 24 | err, 25 | "Invalid Bookmark data", 26 | 500, 27 | ) 28 | return 29 | } 30 | bookmark := &dataResource.Data 31 | // Creates a new DatStore value to working with MongoDB store. 32 | dataStore := common.NewDataStore() 33 | // Add to the mgo.Session.Close() 34 | defer dataStore.Close() 35 | // Get the mgo.Collection for "bookmarks" 36 | col := dataStore.Collection("bookmarks") 37 | // Creates an instance of BookmarkStore 38 | bookmarkStore := store.BookmarkStore{C: col} 39 | // Takes user name from Context 40 | user := r.Context().Value("user") 41 | if user != nil { 42 | bookmark.CreatedBy = user.(string) 43 | } 44 | // Insert a bookmark document 45 | err = bookmarkStore.Create(bookmark) 46 | if err != nil { 47 | common.DisplayAppError( 48 | w, 49 | err, 50 | "Invalid Bookmark data", 51 | 500, 52 | ) 53 | return 54 | } 55 | j, err := json.Marshal(BookmarkResource{Data: *bookmark}) 56 | // If error is occured, 57 | // Send JSON response using helper function common.DisplayAppError 58 | if err != nil { 59 | common.DisplayAppError( 60 | w, 61 | err, 62 | "An unexpected error has occurred", 63 | 500, 64 | ) 65 | return 66 | } 67 | w.Header().Set("Content-Type", "application/json") 68 | w.WriteHeader(http.StatusCreated) 69 | // Write the JSON data to the ResponseWriter 70 | w.Write(j) 71 | 72 | } 73 | 74 | // GetBookmarks returns all Bookmark documents 75 | // Handler for HTTP Get - "/Bookmarks" 76 | func GetBookmarks(w http.ResponseWriter, r *http.Request) { 77 | dataStore := common.NewDataStore() 78 | defer dataStore.Close() 79 | col := dataStore.Collection("bookmarks") 80 | bookmarkStore := store.BookmarkStore{C: col} 81 | bookmarks := bookmarkStore.GetAll() 82 | j, err := json.Marshal(BookmarksResource{Data: bookmarks}) 83 | if err != nil { 84 | common.DisplayAppError( 85 | w, 86 | err, 87 | "An unexpected error has occurred", 88 | 500, 89 | ) 90 | return 91 | } 92 | w.WriteHeader(http.StatusOK) 93 | w.Header().Set("Content-Type", "application/json") 94 | w.Write(j) 95 | } 96 | 97 | // GetBookmarkByID returns a single bookmark document by id 98 | // Handler for HTTP Get - "/Bookmarks/{id}" 99 | func GetBookmarkByID(w http.ResponseWriter, r *http.Request) { 100 | // Get id from the incoming url 101 | vars := mux.Vars(r) 102 | id := vars["id"] 103 | 104 | dataStore := common.NewDataStore() 105 | defer dataStore.Close() 106 | col := dataStore.Collection("bookmarks") 107 | bookmarkStore := store.BookmarkStore{C: col} 108 | 109 | bookmark, err := bookmarkStore.GetByID(id) 110 | if err != nil { 111 | if err == mgo.ErrNotFound { 112 | w.WriteHeader(http.StatusNoContent) 113 | 114 | } else { 115 | common.DisplayAppError( 116 | w, 117 | err, 118 | "An unexpected error has occurred", 119 | 500, 120 | ) 121 | 122 | } 123 | return 124 | } 125 | j, err := json.Marshal(bookmark) 126 | if err != nil { 127 | common.DisplayAppError( 128 | w, 129 | err, 130 | "An unexpected error has occurred", 131 | 500, 132 | ) 133 | return 134 | } 135 | w.Header().Set("Content-Type", "application/json") 136 | w.WriteHeader(http.StatusOK) 137 | w.Write(j) 138 | } 139 | 140 | // GetBookmarksByUser returns all Bookmarks created by a User 141 | // Handler for HTTP Get - "/Bookmarks/users/{id}" 142 | func GetBookmarksByUser(w http.ResponseWriter, r *http.Request) { 143 | // Get id from the incoming url 144 | vars := mux.Vars(r) 145 | user := vars["id"] 146 | dataStore := common.NewDataStore() 147 | defer dataStore.Close() 148 | col := dataStore.Collection("bookmarks") 149 | bookmarkStore := store.BookmarkStore{C: col} 150 | bookmarks := bookmarkStore.GetByUser(user) 151 | j, err := json.Marshal(BookmarksResource{Data: bookmarks}) 152 | if err != nil { 153 | common.DisplayAppError( 154 | w, 155 | err, 156 | "An unexpected error has occurred", 157 | 500, 158 | ) 159 | return 160 | } 161 | w.WriteHeader(http.StatusOK) 162 | w.Header().Set("Content-Type", "application/json") 163 | w.Write(j) 164 | } 165 | 166 | // UpdateBookmark update an existing Bookmark document 167 | // Handler for HTTP Put - "/Bookmarks/{id}" 168 | func UpdateBookmark(w http.ResponseWriter, r *http.Request) { 169 | // Get id from the incoming url 170 | vars := mux.Vars(r) 171 | id := bson.ObjectIdHex(vars["id"]) 172 | var dataResource BookmarkResource 173 | // Decode the incoming Bookmark json 174 | err := json.NewDecoder(r.Body).Decode(&dataResource) 175 | if err != nil { 176 | common.DisplayAppError( 177 | w, 178 | err, 179 | "Invalid Bookmark data", 180 | 500, 181 | ) 182 | return 183 | } 184 | bookmark := dataResource.Data 185 | bookmark.ID = id 186 | dataStore := common.NewDataStore() 187 | defer dataStore.Close() 188 | col := dataStore.Collection("bookmarks") 189 | bookmarkStore := store.BookmarkStore{C: col} 190 | // Update an existing Bookmark document 191 | if err := bookmarkStore.Update(bookmark); err != nil { 192 | common.DisplayAppError( 193 | w, 194 | err, 195 | "An unexpected error has occurred", 196 | 500, 197 | ) 198 | return 199 | } 200 | w.WriteHeader(http.StatusNoContent) 201 | 202 | } 203 | 204 | // DeleteBookmark deletes an existing Bookmark document 205 | // Handler for HTTP Delete - "/Bookmarks/{id}" 206 | func DeleteBookmark(w http.ResponseWriter, r *http.Request) { 207 | vars := mux.Vars(r) 208 | id := vars["id"] 209 | dataStore := common.NewDataStore() 210 | defer dataStore.Close() 211 | col := dataStore.Collection("bookmarks") 212 | bookmarkStore := store.BookmarkStore{C: col} 213 | // Delete an existing Bookmark document 214 | err := bookmarkStore.Delete(id) 215 | if err != nil { 216 | common.DisplayAppError( 217 | w, 218 | err, 219 | "An unexpected error has occurred", 220 | 500, 221 | ) 222 | return 223 | } 224 | w.WriteHeader(http.StatusNoContent) 225 | } 226 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/controllers/resources.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/model" 5 | ) 6 | 7 | //Models for JSON resources 8 | type ( 9 | // UserResource For Post - /user/register 10 | UserResource struct { 11 | Data UserModel `json:"data"` 12 | } 13 | // AuthUserResource Response for authorized user Post - /user/login 14 | AuthUserResource struct { 15 | Data AuthUserModel `json:"data"` 16 | } 17 | // BookmarkResource For Post/Put - /bookmarks 18 | // For Get - /bookmarks/id 19 | BookmarkResource struct { 20 | Data model.Bookmark `json:"data"` 21 | } 22 | // BookmarksResource for Get - /bookmarks 23 | BookmarksResource struct { 24 | Data []model.Bookmark `json:"data"` 25 | } 26 | 27 | // UserModel reperesents a user 28 | UserModel struct { 29 | FirstName string `json:"firstname"` 30 | LastName string `json:"lastname"` 31 | Email string `json:"email"` 32 | Password string `json:"password"` 33 | } 34 | // AuthUserModel for authorized user with access token 35 | AuthUserModel struct { 36 | User model.User `json:"user"` 37 | Token string `json:"token"` 38 | } 39 | ) 40 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/controllers/user_controller.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/common" 8 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/model" 9 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/store" 10 | ) 11 | 12 | // Register add a new User document 13 | // Handler for HTTP Post - "/users/register" 14 | func Register(w http.ResponseWriter, r *http.Request) { 15 | var dataResource UserResource 16 | // Decode the incoming User json 17 | err := json.NewDecoder(r.Body).Decode(&dataResource) 18 | if err != nil { 19 | common.DisplayAppError( 20 | w, 21 | err, 22 | "Invalid User data", 23 | 500, 24 | ) 25 | return 26 | } 27 | userModel := dataResource.Data 28 | dataStore := common.NewDataStore() 29 | defer dataStore.Close() 30 | col := dataStore.Collection("users") 31 | userStore := store.UserStore{C: col} 32 | user := model.User{ 33 | FirstName: userModel.FirstName, 34 | LastName: userModel.LastName, 35 | Email: userModel.Email, 36 | } 37 | // Insert User document 38 | userStore.Create(user, userModel.Password) 39 | w.Header().Set("Content-Type", "application/json") 40 | w.WriteHeader(http.StatusCreated) 41 | } 42 | 43 | // Login authenticates the HTTP request with username and apssword 44 | // Handler for HTTP Post - "/users/login" 45 | func Login(w http.ResponseWriter, r *http.Request) { 46 | var dataResource UserResource 47 | var token string 48 | // Decode the incoming Login json 49 | err := json.NewDecoder(r.Body).Decode(&dataResource) 50 | if err != nil { 51 | common.DisplayAppError( 52 | w, 53 | err, 54 | "Invalid Login data", 55 | 500, 56 | ) 57 | return 58 | } 59 | loginUser := dataResource.Data 60 | dataStore := common.NewDataStore() 61 | defer dataStore.Close() 62 | col := dataStore.Collection("users") 63 | userStore := store.UserStore{C: col} 64 | // Authenticate the login user 65 | user, err := userStore.Login(loginUser.Email, loginUser.Password) 66 | if err != nil { 67 | common.DisplayAppError( 68 | w, 69 | err, 70 | "Invalid login credentials", 71 | 401, 72 | ) 73 | return 74 | } 75 | // Generate JWT token 76 | token, err = common.GenerateJWT(user.Email, "member") 77 | if err != nil { 78 | common.DisplayAppError( 79 | w, 80 | err, 81 | "Eror while generating the access token", 82 | 500, 83 | ) 84 | return 85 | } 86 | w.Header().Set("Content-Type", "application/json") 87 | // Clean-up the hashpassword to eliminate it from response JSON 88 | user.HashPassword = nil 89 | authUser := AuthUserModel{ 90 | User: user, 91 | Token: token, 92 | } 93 | j, err := json.Marshal(AuthUserResource{Data: authUser}) 94 | if err != nil { 95 | common.DisplayAppError( 96 | w, 97 | err, 98 | "An unexpected error has occurred", 99 | 500, 100 | ) 101 | return 102 | } 103 | w.WriteHeader(http.StatusOK) 104 | w.Write(j) 105 | } 106 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/keys/app.rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICWwIBAAKBgQDPSMNALgfEMEDbS3kCLiTlpx0S4tZH1jZLcGF5Pjhc9IuEIf7p 3 | 6obYT1W7urDPWM8JGO1mdx+GnJCnPcvFSsX8FjGwDqIp7afTdislYCJwQLXL7qPz 4 | wvG7ZlrtXrC9+0xkDGNxB+5Cui++8gWGbfTpTZiCiP413esxVQ30btKk2QIDAQAB 5 | AoGACfj1M9RDGWQ86pAB/WHc8pOMqYjySDh9GjoI5n1g1tAJGk1MZ1KaNDP06vg7 6 | Y25hX42sdj6K7L4Bk5o8gHxtc/IsheSeUFbdqbFdiAzTxgHq2zi1ZRPaxtNuw1Wk 7 | KPxcehMpXl/eKCY50+bkVUTvBtfPjRat0fSZnQ4X24zHcAECQQDtU1S/Lu/7vfKI 8 | BR9P2VoCfJvGuwpMcoaXcJuH9oEbeGKnSd7cKVeZdtuapHFjAm/yPWveOYmjrg6a 9 | CiPIgWoBAkEA35hIN/f6wzpLiKPB1TERO/YH1qrRKoM17Y7qcqh8pjmfgY/8we6m 10 | aGrFPd2eUVEpo5XMhQVpsluHmG8ZbXvK2QJAUlfueKM9ixg91WoJkjf03hYEKrDt 11 | Atdd0Z+1pzglVbWwbSDZXYROq6WsznwuB09qLh+XlLRcCFm1IUdRYRleAQJAWQI9 12 | FZKxD5CgSwetfNnom28IlcswMvVCvYvcBsLNxDpCJgiUvPrs4bpHRKZ5hLODmOxk 13 | GzwZZHgNVYA8phnWmQJAER8blPRwsHaEUdPLKWPffvlGPh8RJwpWtaneBOhkyylh 14 | HbPOBvC2WGJ7uXYTiXPHwOeLyRtUYx2GaoKQImi3sw== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/keys/app.rsa.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPSMNALgfEMEDbS3kCLiTlpx0S 3 | 4tZH1jZLcGF5Pjhc9IuEIf7p6obYT1W7urDPWM8JGO1mdx+GnJCnPcvFSsX8FjGw 4 | DqIp7afTdislYCJwQLXL7qPzwvG7ZlrtXrC9+0xkDGNxB+5Cui++8gWGbfTpTZiC 5 | iP413esxVQ30btKk2QIDAQAB 6 | -----END PUBLIC KEY----- 7 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/logs.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shijuvar/go-recipes/1ae7566656200ddab6d66ad478e9feb810690005/ch07/bookmarkapi/logs.txt -------------------------------------------------------------------------------- /ch07/bookmarkapi/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/common" 8 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/routers" 9 | ) 10 | 11 | // Entry point of the program 12 | func main() { 13 | 14 | // Calls startup logic 15 | common.StartUp() 16 | // Get the mux router object 17 | router := routers.InitRoutes() 18 | // Create the Server 19 | server := &http.Server{ 20 | Addr: common.AppConfig.Server, 21 | Handler: router, 22 | } 23 | log.Println("Listening...") 24 | // Running the HTTP Server 25 | server.ListenAndServe() 26 | } 27 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/model/models.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | 6 | "gopkg.in/mgo.v2/bson" 7 | ) 8 | 9 | type ( 10 | // User type represents the registered user. 11 | User struct { 12 | ID bson.ObjectId `bson:"_id,omitempty" json:"id"` 13 | FirstName string `json:"firstname"` 14 | LastName string `json:"lastname"` 15 | Email string `json:"email"` 16 | HashPassword []byte `json:"hashpassword,omitempty"` 17 | } 18 | // Bookmark type represents the metadata of a bookmark. 19 | Bookmark struct { 20 | ID bson.ObjectId `bson:"_id,omitempty"` 21 | Name string `json:"name"` 22 | Description string `json:"description"` 23 | Location string `json:"location"` 24 | Priority int `json:"priority"` // Priority (1 -5) 25 | CreatedBy string `json:"createdby"` 26 | CreatedOn time.Time `json:"createdon,omitempty"` 27 | Tags []string `json:"tags,omitempty"` 28 | } 29 | ) 30 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/routers/bookmark.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "github.com/gorilla/mux" 5 | 6 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/common" 7 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/controllers" 8 | ) 9 | 10 | // SetBookmarkRoutes registers routes for bookmark entity. 11 | func SetBookmarkRoutes(router *mux.Router) *mux.Router { 12 | bookmarkRouter := mux.NewRouter() 13 | bookmarkRouter.HandleFunc("/bookmarks", controllers.CreateBookmark).Methods("POST") 14 | bookmarkRouter.HandleFunc("/bookmarks/{id}", controllers.UpdateBookmark).Methods("PUT") 15 | bookmarkRouter.HandleFunc("/bookmarks", controllers.GetBookmarks).Methods("GET") 16 | bookmarkRouter.HandleFunc("/bookmarks/{id}", controllers.GetBookmarkByID).Methods("GET") 17 | bookmarkRouter.HandleFunc("/bookmarks/users/{id}", controllers.GetBookmarksByUser).Methods("GET") 18 | bookmarkRouter.HandleFunc("/bookmarks/{id}", controllers.DeleteBookmark).Methods("DELETE") 19 | router.PathPrefix("/bookmarks").Handler(common.AuthorizeRequest(bookmarkRouter)) 20 | return router 21 | } 22 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/routers/routers.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "github.com/gorilla/mux" 5 | ) 6 | 7 | // InitRoutes registers all routes for the application. 8 | func InitRoutes() *mux.Router { 9 | router := mux.NewRouter().StrictSlash(false) 10 | // Routes for the User entity 11 | router = SetUserRoutes(router) 12 | // Routes for the Bookmark entity 13 | router = SetBookmarkRoutes(router) 14 | return router 15 | } 16 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/routers/user.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/controllers" 5 | 6 | "github.com/gorilla/mux" 7 | ) 8 | 9 | // SetUserRoutes registers routes for user entity 10 | func SetUserRoutes(router *mux.Router) *mux.Router { 11 | router.HandleFunc("/users", controllers.Register).Methods("POST") 12 | router.HandleFunc("/users/login", controllers.Login).Methods("POST") 13 | return router 14 | } 15 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/store/bookmark_store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "time" 5 | 6 | "gopkg.in/mgo.v2" 7 | "gopkg.in/mgo.v2/bson" 8 | 9 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/model" 10 | ) 11 | 12 | // BookmarkStore provides CRUD operations against the collection "bookmarks". 13 | type BookmarkStore struct { 14 | C *mgo.Collection 15 | } 16 | 17 | // Create inserts the value of struct Bookmark into collection. 18 | func (store BookmarkStore) Create(b *model.Bookmark) error { 19 | // Assign a new bson.ObjectId 20 | b.ID = bson.NewObjectId() 21 | b.CreatedOn = time.Now() 22 | err := store.C.Insert(b) 23 | return err 24 | } 25 | 26 | // Update modifies an existing document of a collection. 27 | func (store BookmarkStore) Update(b model.Bookmark) error { 28 | // partial update on MogoDB 29 | err := store.C.Update(bson.M{"_id": b.ID}, 30 | bson.M{"$set": bson.M{ 31 | "name": b.Name, 32 | "description": b.Description, 33 | "location": b.Location, 34 | "priority": b.Priority, 35 | "tags": b.Tags, 36 | }}) 37 | return err 38 | } 39 | 40 | // Delete removes an existing document from the collection. 41 | func (store BookmarkStore) Delete(id string) error { 42 | err := store.C.Remove(bson.M{"_id": bson.ObjectIdHex(id)}) 43 | return err 44 | } 45 | 46 | // GetAll returns all documents from the collection. 47 | func (store BookmarkStore) GetAll() []model.Bookmark { 48 | var b []model.Bookmark 49 | iter := store.C.Find(nil).Sort("priority", "-createdon").Iter() 50 | result := model.Bookmark{} 51 | for iter.Next(&result) { 52 | b = append(b, result) 53 | } 54 | return b 55 | } 56 | 57 | // GetByUser returns all documents from the collection. 58 | func (store BookmarkStore) GetByUser(user string) []model.Bookmark { 59 | var b []model.Bookmark 60 | iter := store.C.Find(bson.M{"createdby": user}).Sort("priority", "-createdon").Iter() 61 | result := model.Bookmark{} 62 | for iter.Next(&result) { 63 | b = append(b, result) 64 | } 65 | return b 66 | } 67 | 68 | // GetByID returns a single document from the collection. 69 | func (store BookmarkStore) GetByID(id string) (model.Bookmark, error) { 70 | var b model.Bookmark 71 | err := store.C.FindId(bson.ObjectIdHex(id)).One(&b) 72 | return b, err 73 | } 74 | 75 | // GetByTag returns all documents from the collection filtering by tags. 76 | func (store BookmarkStore) GetByTag(tags []string) []model.Bookmark { 77 | var b []model.Bookmark 78 | iter := store.C.Find(bson.M{"tags": bson.M{"$in": tags}}).Sort("priority", "-createdon").Iter() 79 | result := model.Bookmark{} 80 | for iter.Next(&result) { 81 | b = append(b, result) 82 | } 83 | return b 84 | } 85 | -------------------------------------------------------------------------------- /ch07/bookmarkapi/store/user_store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "golang.org/x/crypto/bcrypt" 5 | "gopkg.in/mgo.v2" 6 | "gopkg.in/mgo.v2/bson" 7 | 8 | "github.com/shijuvar/go-recipes/ch07/bookmarkapi/model" 9 | ) 10 | 11 | // UserStore provides persistence logic for "users" collection. 12 | type UserStore struct { 13 | C *mgo.Collection 14 | } 15 | 16 | // Create insert new User 17 | func (store UserStore) Create(user model.User, password string) error { 18 | 19 | user.ID = bson.NewObjectId() 20 | hpass, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) 21 | if err != nil { 22 | return err 23 | } 24 | user.HashPassword = hpass 25 | err = store.C.Insert(user) 26 | return err 27 | } 28 | 29 | // Login authenticates the User 30 | func (store UserStore) Login(email, password string) (model.User, error) { 31 | var user model.User 32 | err := store.C.Find(bson.M{"email": email}).One(&user) 33 | if err != nil { 34 | return model.User{}, err 35 | } 36 | // Validate password 37 | err = bcrypt.CompareHashAndPassword(user.HashPassword, []byte(password)) 38 | if err != nil { 39 | return model.User{}, err 40 | } 41 | return user, nil 42 | } 43 | -------------------------------------------------------------------------------- /ch07/customhandler/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | type textHandler struct { 10 | responseText string 11 | } 12 | 13 | func (th *textHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 14 | fmt.Fprintf(w, th.responseText) 15 | } 16 | 17 | type indexHandler struct { 18 | } 19 | 20 | func (ih *indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 21 | w.Header().Set( 22 | "Content-Type", 23 | "text/html", 24 | ) 25 | html := 26 | ` 27 | 28 | 29 | Hello Gopher 30 | 31 | 32 | Hello Gopher! 33 |

34 | Welcome | Message 35 |

36 | 37 | ` 38 | fmt.Fprintf(w, html) 39 | 40 | } 41 | 42 | func main() { 43 | mux := http.NewServeMux() 44 | mux.Handle("/", &indexHandler{}) 45 | 46 | thWelcome := &textHandler{"Welcome to Go Web Programming"} 47 | mux.Handle("/welcome", thWelcome) 48 | 49 | thMessage := &textHandler{"net/http package is used to build web apps"} 50 | mux.Handle("/message", thMessage) 51 | 52 | log.Println("Listening...") 53 | http.ListenAndServe(":8080", mux) 54 | } 55 | -------------------------------------------------------------------------------- /ch07/defaultservemux/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | func index(w http.ResponseWriter, r *http.Request) { 10 | w.Header().Set( 11 | "Content-Type", 12 | "text/html", 13 | ) 14 | html := 15 | ` 16 | 17 | 18 | Hello Gopher 19 | 20 | 21 | Hello Gopher! 22 |

23 | Welcome | Message 24 |

25 | 26 | ` 27 | fmt.Fprintf(w, html) 28 | } 29 | 30 | func welcome(w http.ResponseWriter, r *http.Request) { 31 | fmt.Fprintf(w, "Welcome to Go Web Programming") 32 | } 33 | func message(w http.ResponseWriter, r *http.Request) { 34 | fmt.Fprintf(w, "net/http package is used to build web apps") 35 | } 36 | 37 | func main() { 38 | http.HandleFunc("/", index) 39 | http.HandleFunc("/welcome", welcome) 40 | http.HandleFunc("/message", message) 41 | log.Println("Listening...") 42 | http.ListenAndServe(":8080", nil) 43 | } 44 | -------------------------------------------------------------------------------- /ch07/handlefunc/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | func index(w http.ResponseWriter, r *http.Request) { 10 | w.Header().Set( 11 | "Content-Type", 12 | "text/html", 13 | ) 14 | html := 15 | ` 16 | 17 | 18 | Hello Gopher 19 | 20 | 21 | Hello Gopher! 22 |

23 | Welcome | Message 24 |

25 | 26 | ` 27 | fmt.Fprintf(w, html) 28 | } 29 | 30 | func welcome(w http.ResponseWriter, r *http.Request) { 31 | fmt.Fprintf(w, "Welcome to Go Web Programming") 32 | } 33 | func message(w http.ResponseWriter, r *http.Request) { 34 | fmt.Fprintf(w, "net/http package is used to build web apps") 35 | } 36 | 37 | func main() { 38 | mux := http.NewServeMux() 39 | mux.HandleFunc("/", index) 40 | mux.HandleFunc("/welcome", welcome) 41 | mux.HandleFunc("/message", message) 42 | log.Println("Listening...") 43 | http.ListenAndServe(":8080", mux) 44 | } 45 | -------------------------------------------------------------------------------- /ch07/handlerfunc/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | func textResponseHandler(resposeText string) http.Handler { 10 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 11 | fmt.Fprintf(w, resposeText) 12 | }) 13 | } 14 | func index(w http.ResponseWriter, r *http.Request) { 15 | w.Header().Set( 16 | "Content-Type", 17 | "text/html", 18 | ) 19 | html := 20 | ` 21 | 22 | 23 | Hello Gopher 24 | 25 | 26 | Hello Gopher! 27 |

28 | Welcome | Message 29 |

30 | 31 | ` 32 | fmt.Fprintf(w, html) 33 | } 34 | 35 | func welcome(w http.ResponseWriter, r *http.Request) { 36 | fmt.Fprintf(w, "Welcome to Go Web Programming") 37 | } 38 | func message(w http.ResponseWriter, r *http.Request) { 39 | fmt.Fprintf(w, "net/http package is used to build web apps") 40 | } 41 | 42 | func main() { 43 | mux := http.NewServeMux() 44 | mux.Handle("/", http.HandlerFunc(index)) 45 | mux.Handle("/welcome", http.HandlerFunc(welcome)) 46 | mux.Handle("/message", http.HandlerFunc(message)) 47 | //mux.Handle("/welcome", textResponseHandler("Welcome to Go Web Programming")) 48 | //mux.Handle("/message", textResponseHandler("net/http package is used to build web apps")) 49 | 50 | log.Println("Listening...") 51 | http.ListenAndServe(":8080", mux) 52 | } 53 | -------------------------------------------------------------------------------- /ch07/httpserver/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | func handler(w http.ResponseWriter, r *http.Request) { 9 | fmt.Fprint(w, "Hello, world!") 10 | } 11 | 12 | func main() { 13 | http.HandleFunc("/", handler) 14 | http.ListenAndServe(":8080", nil) 15 | } 16 | -------------------------------------------------------------------------------- /ch07/middleware/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "time" 8 | ) 9 | 10 | // loggingHandler is an HTTP Middleware that logs HTTP requests. 11 | func loggingHandler(next http.Handler) http.Handler { 12 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 13 | // Middleware logic before executing given Handler 14 | start := time.Now() 15 | log.Printf("Started %s %s", r.Method, r.URL.Path) 16 | next.ServeHTTP(w, r) 17 | // Middleware logic after executing given Handler 18 | log.Printf("Completed %s in %v", r.URL.Path, time.Since(start)) 19 | }) 20 | } 21 | 22 | func index(w http.ResponseWriter, r *http.Request) { 23 | w.Header().Set( 24 | "Content-Type", 25 | "text/html", 26 | ) 27 | html := 28 | ` 29 | 30 | 31 | Hello Gopher 32 | 33 | 34 | Hello Gopher! 35 |

36 | Welcome | Message 37 |

38 | 39 | ` 40 | fmt.Fprintf(w, html) 41 | } 42 | 43 | func welcome(w http.ResponseWriter, r *http.Request) { 44 | fmt.Fprintf(w, "Welcome to Go Web Programming") 45 | } 46 | func message(w http.ResponseWriter, r *http.Request) { 47 | fmt.Fprintf(w, "net/http package is used to build web apps") 48 | } 49 | func favicon(w http.ResponseWriter, r *http.Request) { 50 | } 51 | 52 | func main() { 53 | http.HandleFunc("/favicon.ico", favicon) 54 | http.Handle("/", loggingHandler(http.HandlerFunc(index))) 55 | http.Handle("/welcome", loggingHandler(http.HandlerFunc(welcome))) 56 | http.Handle("/message", loggingHandler(http.HandlerFunc(message))) 57 | log.Println("Listening...") 58 | http.ListenAndServe(":8080", nil) 59 | } 60 | -------------------------------------------------------------------------------- /ch07/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "time" 8 | ) 9 | 10 | func index(w http.ResponseWriter, r *http.Request) { 11 | fmt.Fprintf(w, "Welcome to Go Web Programming") 12 | } 13 | 14 | func main() { 15 | 16 | http.HandleFunc("/", index) 17 | 18 | server := &http.Server{ 19 | Addr: ":8080", 20 | ReadTimeout: 60 * time.Second, 21 | WriteTimeout: 60 * time.Second, 22 | } 23 | 24 | log.Println("Listening...") 25 | server.ListenAndServe() 26 | } 27 | -------------------------------------------------------------------------------- /ch08/calc/calc.go: -------------------------------------------------------------------------------- 1 | // Package calc provides a simple calculator 2 | package calc 3 | 4 | import "math" 5 | 6 | // Sum returns sum of integer values 7 | func Sum(nums ...int) int { 8 | result := 0 9 | for _, v := range nums { 10 | result += v 11 | } 12 | return result 13 | } 14 | 15 | // Average returns average of integer values 16 | // The output provides a float64 value in two decimal points 17 | func Average(nums ...int) float64 { 18 | sum := 0 19 | for _, v := range nums { 20 | sum += v 21 | } 22 | result := float64(sum) / float64(len(nums)) 23 | pow := math.Pow(10, float64(2)) 24 | digit := pow * result 25 | round := math.Floor(digit) 26 | return round / pow 27 | 28 | } 29 | -------------------------------------------------------------------------------- /ch08/calc/calc_test.go: -------------------------------------------------------------------------------- 1 | package calc 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | // Test case for the function Sum 10 | func TestSum(t *testing.T) { 11 | input, expected := []int{7, 8, 10}, 25 12 | result := Sum(input...) 13 | if result != expected { 14 | 15 | t.Errorf("Result: %d, Expected: %d", result, expected) 16 | } 17 | 18 | } 19 | 20 | // Test case for function Average 21 | func TestAverage(t *testing.T) { 22 | input, expected := []int{7, 8, 10}, 8.33 23 | result := Average(input...) 24 | if result != expected { 25 | 26 | t.Errorf("Result: %f, Expected: %f", result, expected) 27 | } 28 | } 29 | 30 | // Benchmark for function Sum 31 | func BenchmarkSum(b *testing.B) { 32 | for i := 0; i < b.N; i++ { 33 | Sum(7, 8, 10) 34 | } 35 | } 36 | 37 | // Benchmark for function Average 38 | func BenchmarkAverage(b *testing.B) { 39 | for i := 0; i < b.N; i++ { 40 | Average(7, 8, 10) 41 | } 42 | } 43 | 44 | func TestLongRun(t *testing.T) { 45 | // Checks whether the short flag is provided 46 | if testing.Short() { 47 | t.Skip("Skipping test in short mode") 48 | } 49 | // Long running implementation goes here 50 | time.Sleep(5 * time.Second) 51 | } 52 | 53 | // Test case for the function Sum to be executed in parallel 54 | func TestSumInParallel(t *testing.T) { 55 | t.Parallel() 56 | // Delaying 1 second for the sake of demonstration 57 | time.Sleep(1 * time.Second) 58 | input, expected := []int{7, 8, 10}, 25 59 | result := Sum(input...) 60 | if result != expected { 61 | 62 | t.Errorf("Result: %d, Expected: %d", result, expected) 63 | } 64 | 65 | } 66 | 67 | // Test case for the function Sum to be executed in parallel 68 | func TestAverageInParallel(t *testing.T) { 69 | t.Parallel() 70 | // Delaying 1 second for the sake of demonstration 71 | time.Sleep(2 * time.Second) 72 | input, expected := []int{7, 8, 10}, 8.33 73 | result := Average(input...) 74 | if result != expected { 75 | 76 | t.Errorf("Result: %f, Expected: %f", result, expected) 77 | } 78 | } 79 | 80 | // Example code for function Sum 81 | func ExampleSum() { 82 | fmt.Println(Sum(7, 8, 10)) 83 | // Output: 25 84 | } 85 | 86 | // Example code for function Average 87 | func ExampleAverage() { 88 | fmt.Println(Average(7, 8, 10)) 89 | // Output: 8.33 90 | } 91 | -------------------------------------------------------------------------------- /ch08/httpbdd/controllers/controllers_suite_test.go: -------------------------------------------------------------------------------- 1 | package controllers_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestControllers(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Controllers Suite") 13 | } 14 | -------------------------------------------------------------------------------- /ch08/httpbdd/controllers/user_controller.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "net/http" 7 | 8 | "github.com/shijuvar/go-recipes/ch08/httpbdd/model" 9 | ) 10 | 11 | // GetUsers serves requests for Http Get to "/users" 12 | func GetUsers(store model.UserStore) http.Handler { 13 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 14 | data := store.GetUsers() 15 | users, err := json.Marshal(data) 16 | if err != nil { 17 | w.WriteHeader(http.StatusInternalServerError) 18 | return 19 | } 20 | w.Header().Set("Content-Type", "application/json") 21 | w.WriteHeader(http.StatusOK) 22 | w.Write(users) 23 | }) 24 | 25 | } 26 | 27 | // CreateUser serves requests for Http Post to "/users" 28 | func CreateUser(store model.UserStore) http.Handler { 29 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 30 | var user model.User 31 | // Decode the incoming User json 32 | err := json.NewDecoder(r.Body).Decode(&user) 33 | if err != nil { 34 | log.Fatalf("[Controllers.CreateUser]: %s\n", err) 35 | w.WriteHeader(http.StatusInternalServerError) 36 | return 37 | } 38 | // Insert User entity into User Store 39 | err = store.AddUser(user) 40 | if err != nil { 41 | if err == model.ErrorEmailExists { 42 | w.WriteHeader(http.StatusBadRequest) 43 | } else { 44 | w.WriteHeader(http.StatusInternalServerError) 45 | } 46 | return 47 | } 48 | w.WriteHeader(http.StatusCreated) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /ch08/httpbdd/controllers/user_controller_test.go: -------------------------------------------------------------------------------- 1 | package controllers_test 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | "net/http/httptest" 7 | "strings" 8 | 9 | "github.com/shijuvar/go-recipes/ch08/httpbdd/controllers" 10 | "github.com/shijuvar/go-recipes/ch08/httpbdd/model" 11 | 12 | "github.com/gorilla/mux" 13 | . "github.com/onsi/ginkgo" 14 | . "github.com/onsi/gomega" 15 | ) 16 | 17 | var _ = Describe("UserController", func() { 18 | var r *mux.Router 19 | var w *httptest.ResponseRecorder 20 | var store *FakeUserStore 21 | BeforeEach(func() { 22 | r = mux.NewRouter() 23 | store = newFakeUserStore() 24 | }) 25 | 26 | // Specs for HTTP Get to "/users" 27 | Describe("Get list of Users", func() { 28 | Context("Get all Users from data store", func() { 29 | It("Should get list of Users", func() { 30 | r.Handle("/users", controllers.GetUsers(store)).Methods("GET") 31 | req, err := http.NewRequest("GET", "/users", nil) 32 | Expect(err).NotTo(HaveOccurred()) 33 | w = httptest.NewRecorder() 34 | r.ServeHTTP(w, req) 35 | Expect(w.Code).To(Equal(200)) 36 | var users []model.User 37 | json.Unmarshal(w.Body.Bytes(), &users) 38 | // Verifying mocked data of 2 users 39 | Expect(len(users)).To(Equal(2)) 40 | }) 41 | }) 42 | }) 43 | 44 | // Specs for HTTP Post to "/users" 45 | Describe("Post a new User", func() { 46 | Context("Provide a valid User data", func() { 47 | It("Should create a new User and get HTTP Status: 201", func() { 48 | r.Handle("/users", controllers.CreateUser(store)).Methods("POST") 49 | userJson := `{"firstname": "Alex", "lastname": "John", "email": "alex@xyz.com"}` 50 | 51 | req, err := http.NewRequest( 52 | "POST", 53 | "/users", 54 | strings.NewReader(userJson), 55 | ) 56 | Expect(err).NotTo(HaveOccurred()) 57 | w = httptest.NewRecorder() 58 | r.ServeHTTP(w, req) 59 | Expect(w.Code).To(Equal(201)) 60 | }) 61 | }) 62 | Context("Provide a User data that contains duplicate email id", func() { 63 | It("Should get HTTP Status: 400", func() { 64 | r.Handle("/users", controllers.CreateUser(store)).Methods("POST") 65 | userJson := `{"firstname": "Shiju", "lastname": "Varghese", "email": "shiju@xyz.com"}` 66 | 67 | req, err := http.NewRequest( 68 | "POST", 69 | "/users", 70 | strings.NewReader(userJson), 71 | ) 72 | Expect(err).NotTo(HaveOccurred()) 73 | w = httptest.NewRecorder() 74 | r.ServeHTTP(w, req) 75 | Expect(w.Code).To(Equal(400)) 76 | }) 77 | }) 78 | }) 79 | }) 80 | 81 | // FakeUserStore provides a mocked implementation of interface model.UserStore 82 | type FakeUserStore struct { 83 | userStore []model.User 84 | } 85 | 86 | // GetUsers returns all users 87 | func (store *FakeUserStore) GetUsers() []model.User { 88 | return store.userStore 89 | } 90 | 91 | // AddUser inserts a User 92 | func (store *FakeUserStore) AddUser(user model.User) error { 93 | // Check whether email is exists 94 | for _, u := range store.userStore { 95 | if u.Email == user.Email { 96 | return model.ErrorEmailExists 97 | } 98 | } 99 | store.userStore = append(store.userStore, user) 100 | return nil 101 | } 102 | 103 | // newFakeUserStore provides two dummy data for Users 104 | func newFakeUserStore() *FakeUserStore { 105 | store := &FakeUserStore{} 106 | store.AddUser(model.User{ 107 | FirstName: "Shiju", 108 | LastName: "Varghese", 109 | Email: "shiju@xyz.com", 110 | }) 111 | 112 | store.AddUser(model.User{ 113 | FirstName: "Irene", 114 | LastName: "Rose", 115 | Email: "irene@xyz.com", 116 | }) 117 | return store 118 | } 119 | -------------------------------------------------------------------------------- /ch08/httpbdd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gorilla/mux" 7 | 8 | "github.com/shijuvar/go-recipes/ch08/httpbdd/controllers" 9 | "github.com/shijuvar/go-recipes/ch08/httpbdd/store" 10 | ) 11 | 12 | func setUserRoutes() *mux.Router { 13 | r := mux.NewRouter() 14 | userStore := &store.MongoUserStore{} 15 | r.Handle("/users", controllers.CreateUser(userStore)).Methods("POST") 16 | r.Handle("/users", controllers.GetUsers(userStore)).Methods("GET") 17 | return r 18 | } 19 | 20 | func main() { 21 | http.ListenAndServe(":8080", setUserRoutes()) 22 | } 23 | -------------------------------------------------------------------------------- /ch08/httpbdd/model/user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "errors" 4 | 5 | // ErrorEmailExists is an error value for duplicate email id 6 | var ErrorEmailExists = errors.New("Email Id is exists") 7 | 8 | // User model 9 | type User struct { 10 | FirstName string `json:"firstname"` 11 | LastName string `json:"lastname"` 12 | Email string `json:"email"` 13 | } 14 | 15 | // UserStore provides a contract for Data Store for User entity 16 | type UserStore interface { 17 | GetUsers() []User 18 | AddUser(User) error 19 | } 20 | -------------------------------------------------------------------------------- /ch08/httpbdd/store/user_store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "gopkg.in/mgo.v2" 8 | "gopkg.in/mgo.v2/bson" 9 | 10 | "github.com/shijuvar/go-recipes/ch08/httpbdd/model" 11 | ) 12 | 13 | // MongoDB Session 14 | var mgoSession *mgo.Session 15 | 16 | // Create a MongoDB Session 17 | func createDBSession() { 18 | var err error 19 | mgoSession, err = mgo.DialWithInfo(&mgo.DialInfo{ 20 | Addrs: []string{"127.0.0.1"}, 21 | Timeout: 60 * time.Second, 22 | }) 23 | if err != nil { 24 | log.Fatalf("[createDbSession]: %s\n", err) 25 | } 26 | } 27 | 28 | // Initializes the MongoDB Session 29 | func init() { 30 | createDBSession() 31 | } 32 | 33 | // MongoUserStore provides persistence logic for "users" collection. 34 | type MongoUserStore struct{} 35 | 36 | // AddUser insert new User 37 | func (store *MongoUserStore) AddUser(user model.User) error { 38 | session := mgoSession.Copy() 39 | defer session.Close() 40 | userCol := session.DB("userdb").C("users") 41 | // Check whether email id is exists or not 42 | var existUser model.User 43 | err := userCol.Find(bson.M{"email": user.Email}).One(&existUser) 44 | if err != nil { 45 | if err == mgo.ErrNotFound { // Email is unique, no records found 46 | } 47 | } 48 | if (model.User{}) != existUser { // there is a user 49 | return model.ErrorEmailExists 50 | } 51 | err = userCol.Insert(user) 52 | return err 53 | } 54 | 55 | // GetUsers returns all documents from the collection. 56 | func (store *MongoUserStore) GetUsers() []model.User { 57 | session := mgoSession.Copy() 58 | defer session.Close() 59 | userCol := session.DB("userdb").C("users") 60 | var users []model.User 61 | iter := userCol.Find(nil).Iter() 62 | result := model.User{} 63 | for iter.Next(&result) { 64 | users = append(users, result) 65 | } 66 | return users 67 | } 68 | -------------------------------------------------------------------------------- /ch08/httptest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/gorilla/mux" 8 | ) 9 | 10 | // User model 11 | type User struct { 12 | FirstName string `json:"firstname"` 13 | LastName string `json:"lastname"` 14 | Email string `json:"email"` 15 | } 16 | 17 | // getUsers serves requests for Http Get to "/users" 18 | func getUsers(w http.ResponseWriter, r *http.Request) { 19 | data := []User{ 20 | User{ 21 | FirstName: "Shiju", 22 | LastName: "Varghese", 23 | Email: "shiju@xyz.com", 24 | }, 25 | 26 | User{ 27 | FirstName: "Irene", 28 | LastName: "Rose", 29 | Email: "irene@xyz.com", 30 | }, 31 | } 32 | users, err := json.Marshal(data) 33 | if err != nil { 34 | w.WriteHeader(http.StatusInternalServerError) 35 | return 36 | } 37 | w.Header().Set("Content-Type", "application/json") 38 | w.WriteHeader(http.StatusOK) 39 | w.Write(users) 40 | } 41 | 42 | func main() { 43 | r := mux.NewRouter() 44 | r.HandleFunc("/users", getUsers).Methods("GET") 45 | http.ListenAndServe(":8080", r) 46 | } 47 | -------------------------------------------------------------------------------- /ch08/httptest/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | 9 | "github.com/gorilla/mux" 10 | ) 11 | 12 | // TestGetUsers test HTTP Get to "/users" using ResponseRecorder 13 | func TestGetUsers(t *testing.T) { 14 | r := mux.NewRouter() 15 | r.HandleFunc("/users", getUsers).Methods("GET") 16 | req, err := http.NewRequest("GET", "/users", nil) 17 | if err != nil { 18 | t.Error(err) 19 | } 20 | w := httptest.NewRecorder() 21 | 22 | r.ServeHTTP(w, req) 23 | if w.Code != 200 { 24 | t.Errorf("HTTP Status expected: 200, got: %d", w.Code) 25 | } 26 | } 27 | 28 | // TestGetUsersWithServer test HTTP Get to "/users" using Server 29 | func TestGetUsersWithServer(t *testing.T) { 30 | r := mux.NewRouter() 31 | r.HandleFunc("/users", getUsers).Methods("GET") 32 | server := httptest.NewServer(r) 33 | defer server.Close() 34 | usersURL := fmt.Sprintf("%s/users", server.URL) 35 | request, err := http.NewRequest("GET", usersURL, nil) 36 | 37 | res, err := http.DefaultClient.Do(request) 38 | 39 | if err != nil { 40 | t.Error(err) 41 | } 42 | 43 | if res.StatusCode != 200 { 44 | t.Errorf("HTTP Status expected: 200, got: %d", res.StatusCode) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /grpc/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | 7 | "golang.org/x/net/context" 8 | "google.golang.org/grpc" 9 | 10 | pb "github.com/shijuvar/go-recipes/grpc/customer" 11 | ) 12 | 13 | const ( 14 | address = "localhost:50051" 15 | ) 16 | 17 | // createCustomer calls the RPC method CreateCustomer of CustomerServer 18 | func createCustomer(client pb.CustomerClient, customer *pb.CustomerRequest) { 19 | resp, err := client.CreateCustomer(context.Background(), customer) 20 | if err != nil { 21 | log.Fatalf("Could not create Customer: %v", err) 22 | } 23 | if resp.Success { 24 | log.Printf("A new Customer has been added with id: %d", resp.Id) 25 | } 26 | } 27 | 28 | // getCustomers calls the RPC method GetCustomers of CustomerServer 29 | func getCustomers(client pb.CustomerClient, filter *pb.CustomerFilter) { 30 | // calling the streaming API 31 | stream, err := client.GetCustomers(context.Background(), filter) 32 | if err != nil { 33 | log.Fatalf("Error on get customers: %v", err) 34 | } 35 | for { 36 | // Receiving the stream of data 37 | customer, err := stream.Recv() 38 | if err == io.EOF { 39 | break 40 | } 41 | if err != nil { 42 | log.Fatalf("%v.GetCustomers(_) = _, %v", client, err) 43 | } 44 | log.Printf("Customer: %v", customer) 45 | } 46 | } 47 | func main() { 48 | // Set up a connection to the gRPC server. 49 | conn, err := grpc.Dial(address, grpc.WithInsecure()) 50 | if err != nil { 51 | log.Fatalf("did not connect: %v", err) 52 | } 53 | defer conn.Close() 54 | // Creates a new CustomerClient 55 | client := pb.NewCustomerClient(conn) 56 | 57 | customer := &pb.CustomerRequest{ 58 | Id: 101, 59 | Name: "Shiju Varghese", 60 | Email: "shiju@xyz.com", 61 | Phone: "732-757-2923", 62 | Addresses: []*pb.CustomerRequest_Address{ 63 | &pb.CustomerRequest_Address{ 64 | Street: "1 Mission Street", 65 | City: "San Francisco", 66 | State: "CA", 67 | Zip: "94105", 68 | IsShippingAddress: false, 69 | }, 70 | &pb.CustomerRequest_Address{ 71 | Street: "Greenfield", 72 | City: "Kochi", 73 | State: "KL", 74 | Zip: "68356", 75 | IsShippingAddress: true, 76 | }, 77 | }, 78 | } 79 | 80 | // Create a new customer 81 | createCustomer(client, customer) 82 | 83 | customer = &pb.CustomerRequest{ 84 | Id: 102, 85 | Name: "Irene Rose", 86 | Email: "irene@xyz.com", 87 | Phone: "732-757-2924", 88 | Addresses: []*pb.CustomerRequest_Address{ 89 | &pb.CustomerRequest_Address{ 90 | Street: "1 Mission Street", 91 | City: "San Francisco", 92 | State: "CA", 93 | Zip: "94105", 94 | IsShippingAddress: true, 95 | }, 96 | }, 97 | } 98 | 99 | // Create a new customer 100 | createCustomer(client, customer) 101 | // Filter with an empty Keyword 102 | filter := &pb.CustomerFilter{Keyword: ""} 103 | getCustomers(client, filter) 104 | } 105 | -------------------------------------------------------------------------------- /grpc/customer/customer.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: customer.proto 3 | // DO NOT EDIT! 4 | 5 | /* 6 | Package customer is a generated protocol buffer package. 7 | 8 | It is generated from these files: 9 | customer.proto 10 | 11 | It has these top-level messages: 12 | CustomerRequest 13 | CustomerResponse 14 | CustomerFilter 15 | */ 16 | package customer 17 | 18 | import proto "github.com/golang/protobuf/proto" 19 | import fmt "fmt" 20 | import math "math" 21 | 22 | import ( 23 | context "golang.org/x/net/context" 24 | grpc "google.golang.org/grpc" 25 | ) 26 | 27 | // Reference imports to suppress errors if they are not otherwise used. 28 | var _ = proto.Marshal 29 | var _ = fmt.Errorf 30 | var _ = math.Inf 31 | 32 | // This is a compile-time assertion to ensure that this generated file 33 | // is compatible with the proto package it is being compiled against. 34 | // A compilation error at this line likely means your copy of the 35 | // proto package needs to be updated. 36 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 37 | 38 | // Request message for creating a new customer 39 | type CustomerRequest struct { 40 | Id int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` 41 | Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` 42 | Email string `protobuf:"bytes,3,opt,name=email" json:"email,omitempty"` 43 | Phone string `protobuf:"bytes,4,opt,name=phone" json:"phone,omitempty"` 44 | Addresses []*CustomerRequest_Address `protobuf:"bytes,5,rep,name=addresses" json:"addresses,omitempty"` 45 | } 46 | 47 | func (m *CustomerRequest) Reset() { *m = CustomerRequest{} } 48 | func (m *CustomerRequest) String() string { return proto.CompactTextString(m) } 49 | func (*CustomerRequest) ProtoMessage() {} 50 | func (*CustomerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 51 | 52 | func (m *CustomerRequest) GetAddresses() []*CustomerRequest_Address { 53 | if m != nil { 54 | return m.Addresses 55 | } 56 | return nil 57 | } 58 | 59 | type CustomerRequest_Address struct { 60 | Street string `protobuf:"bytes,1,opt,name=street" json:"street,omitempty"` 61 | City string `protobuf:"bytes,2,opt,name=city" json:"city,omitempty"` 62 | State string `protobuf:"bytes,3,opt,name=state" json:"state,omitempty"` 63 | Zip string `protobuf:"bytes,4,opt,name=zip" json:"zip,omitempty"` 64 | IsShippingAddress bool `protobuf:"varint,5,opt,name=isShippingAddress" json:"isShippingAddress,omitempty"` 65 | } 66 | 67 | func (m *CustomerRequest_Address) Reset() { *m = CustomerRequest_Address{} } 68 | func (m *CustomerRequest_Address) String() string { return proto.CompactTextString(m) } 69 | func (*CustomerRequest_Address) ProtoMessage() {} 70 | func (*CustomerRequest_Address) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} } 71 | 72 | type CustomerResponse struct { 73 | Id int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` 74 | Success bool `protobuf:"varint,2,opt,name=success" json:"success,omitempty"` 75 | } 76 | 77 | func (m *CustomerResponse) Reset() { *m = CustomerResponse{} } 78 | func (m *CustomerResponse) String() string { return proto.CompactTextString(m) } 79 | func (*CustomerResponse) ProtoMessage() {} 80 | func (*CustomerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 81 | 82 | type CustomerFilter struct { 83 | Keyword string `protobuf:"bytes,1,opt,name=keyword" json:"keyword,omitempty"` 84 | } 85 | 86 | func (m *CustomerFilter) Reset() { *m = CustomerFilter{} } 87 | func (m *CustomerFilter) String() string { return proto.CompactTextString(m) } 88 | func (*CustomerFilter) ProtoMessage() {} 89 | func (*CustomerFilter) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 90 | 91 | func init() { 92 | proto.RegisterType((*CustomerRequest)(nil), "customer.CustomerRequest") 93 | proto.RegisterType((*CustomerRequest_Address)(nil), "customer.CustomerRequest.Address") 94 | proto.RegisterType((*CustomerResponse)(nil), "customer.CustomerResponse") 95 | proto.RegisterType((*CustomerFilter)(nil), "customer.CustomerFilter") 96 | } 97 | 98 | // Reference imports to suppress errors if they are not otherwise used. 99 | var _ context.Context 100 | var _ grpc.ClientConn 101 | 102 | // This is a compile-time assertion to ensure that this generated file 103 | // is compatible with the grpc package it is being compiled against. 104 | const _ = grpc.SupportPackageIsVersion3 105 | 106 | // Client API for Customer service 107 | 108 | type CustomerClient interface { 109 | // Get all Customers with filter - A server-to-client streaming RPC. 110 | GetCustomers(ctx context.Context, in *CustomerFilter, opts ...grpc.CallOption) (Customer_GetCustomersClient, error) 111 | // Create a new Customer - A simple RPC 112 | CreateCustomer(ctx context.Context, in *CustomerRequest, opts ...grpc.CallOption) (*CustomerResponse, error) 113 | } 114 | 115 | type customerClient struct { 116 | cc *grpc.ClientConn 117 | } 118 | 119 | func NewCustomerClient(cc *grpc.ClientConn) CustomerClient { 120 | return &customerClient{cc} 121 | } 122 | 123 | func (c *customerClient) GetCustomers(ctx context.Context, in *CustomerFilter, opts ...grpc.CallOption) (Customer_GetCustomersClient, error) { 124 | stream, err := grpc.NewClientStream(ctx, &_Customer_serviceDesc.Streams[0], c.cc, "/customer.Customer/GetCustomers", opts...) 125 | if err != nil { 126 | return nil, err 127 | } 128 | x := &customerGetCustomersClient{stream} 129 | if err := x.ClientStream.SendMsg(in); err != nil { 130 | return nil, err 131 | } 132 | if err := x.ClientStream.CloseSend(); err != nil { 133 | return nil, err 134 | } 135 | return x, nil 136 | } 137 | 138 | type Customer_GetCustomersClient interface { 139 | Recv() (*CustomerRequest, error) 140 | grpc.ClientStream 141 | } 142 | 143 | type customerGetCustomersClient struct { 144 | grpc.ClientStream 145 | } 146 | 147 | func (x *customerGetCustomersClient) Recv() (*CustomerRequest, error) { 148 | m := new(CustomerRequest) 149 | if err := x.ClientStream.RecvMsg(m); err != nil { 150 | return nil, err 151 | } 152 | return m, nil 153 | } 154 | 155 | func (c *customerClient) CreateCustomer(ctx context.Context, in *CustomerRequest, opts ...grpc.CallOption) (*CustomerResponse, error) { 156 | out := new(CustomerResponse) 157 | err := grpc.Invoke(ctx, "/customer.Customer/CreateCustomer", in, out, c.cc, opts...) 158 | if err != nil { 159 | return nil, err 160 | } 161 | return out, nil 162 | } 163 | 164 | // Server API for Customer service 165 | 166 | type CustomerServer interface { 167 | // Get all Customers with filter - A server-to-client streaming RPC. 168 | GetCustomers(*CustomerFilter, Customer_GetCustomersServer) error 169 | // Create a new Customer - A simple RPC 170 | CreateCustomer(context.Context, *CustomerRequest) (*CustomerResponse, error) 171 | } 172 | 173 | func RegisterCustomerServer(s *grpc.Server, srv CustomerServer) { 174 | s.RegisterService(&_Customer_serviceDesc, srv) 175 | } 176 | 177 | func _Customer_GetCustomers_Handler(srv interface{}, stream grpc.ServerStream) error { 178 | m := new(CustomerFilter) 179 | if err := stream.RecvMsg(m); err != nil { 180 | return err 181 | } 182 | return srv.(CustomerServer).GetCustomers(m, &customerGetCustomersServer{stream}) 183 | } 184 | 185 | type Customer_GetCustomersServer interface { 186 | Send(*CustomerRequest) error 187 | grpc.ServerStream 188 | } 189 | 190 | type customerGetCustomersServer struct { 191 | grpc.ServerStream 192 | } 193 | 194 | func (x *customerGetCustomersServer) Send(m *CustomerRequest) error { 195 | return x.ServerStream.SendMsg(m) 196 | } 197 | 198 | func _Customer_CreateCustomer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 199 | in := new(CustomerRequest) 200 | if err := dec(in); err != nil { 201 | return nil, err 202 | } 203 | if interceptor == nil { 204 | return srv.(CustomerServer).CreateCustomer(ctx, in) 205 | } 206 | info := &grpc.UnaryServerInfo{ 207 | Server: srv, 208 | FullMethod: "/customer.Customer/CreateCustomer", 209 | } 210 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 211 | return srv.(CustomerServer).CreateCustomer(ctx, req.(*CustomerRequest)) 212 | } 213 | return interceptor(ctx, in, info, handler) 214 | } 215 | 216 | var _Customer_serviceDesc = grpc.ServiceDesc{ 217 | ServiceName: "customer.Customer", 218 | HandlerType: (*CustomerServer)(nil), 219 | Methods: []grpc.MethodDesc{ 220 | { 221 | MethodName: "CreateCustomer", 222 | Handler: _Customer_CreateCustomer_Handler, 223 | }, 224 | }, 225 | Streams: []grpc.StreamDesc{ 226 | { 227 | StreamName: "GetCustomers", 228 | Handler: _Customer_GetCustomers_Handler, 229 | ServerStreams: true, 230 | }, 231 | }, 232 | Metadata: fileDescriptor0, 233 | } 234 | 235 | func init() { proto.RegisterFile("customer.proto", fileDescriptor0) } 236 | 237 | var fileDescriptor0 = []byte{ 238 | // 326 bytes of a gzipped FileDescriptorProto 239 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x92, 0xef, 0x4a, 0xc3, 0x30, 240 | 0x10, 0xc0, 0x97, 0x6e, 0xdd, 0x9f, 0x53, 0xea, 0x0c, 0x22, 0xb1, 0x9f, 0x6a, 0x3f, 0x15, 0x91, 241 | 0x21, 0xf3, 0xab, 0x20, 0x32, 0x70, 0xf8, 0xb5, 0x3e, 0x41, 0x6d, 0x0f, 0x17, 0xdc, 0xda, 0x9a, 242 | 0xcb, 0x90, 0xf9, 0x0a, 0xbe, 0x83, 0xcf, 0xe0, 0x23, 0x4a, 0xd2, 0x66, 0x03, 0xe7, 0xbe, 0xdd, 243 | 0xef, 0x72, 0x77, 0xf9, 0xe5, 0x08, 0x04, 0xf9, 0x9a, 0x74, 0xb5, 0x42, 0x35, 0xa9, 0x55, 0xa5, 244 | 0x2b, 0x3e, 0x74, 0x1c, 0xff, 0x78, 0x70, 0x32, 0x6b, 0x21, 0xc5, 0xf7, 0x35, 0x92, 0xe6, 0x01, 245 | 0x78, 0xb2, 0x10, 0x2c, 0x62, 0x89, 0x9f, 0x7a, 0xb2, 0xe0, 0x1c, 0x7a, 0x65, 0xb6, 0x42, 0xe1, 246 | 0x45, 0x2c, 0x19, 0xa5, 0x36, 0xe6, 0x67, 0xe0, 0xe3, 0x2a, 0x93, 0x4b, 0xd1, 0xb5, 0xc9, 0x06, 247 | 0x4c, 0xb6, 0x5e, 0x54, 0x25, 0x8a, 0x5e, 0x93, 0xb5, 0xc0, 0xef, 0x61, 0x94, 0x15, 0x85, 0x42, 248 | 0x22, 0x24, 0xe1, 0x47, 0xdd, 0xe4, 0x68, 0x7a, 0x39, 0xd9, 0x1a, 0xfd, 0xb9, 0x7d, 0xf2, 0xd0, 249 | 0x94, 0xa6, 0xbb, 0x9e, 0xf0, 0x8b, 0xc1, 0xa0, 0x4d, 0xf3, 0x73, 0xe8, 0x93, 0x56, 0x88, 0xda, 250 | 0x0a, 0x8e, 0xd2, 0x96, 0x8c, 0x64, 0x2e, 0xf5, 0xc6, 0x49, 0x9a, 0xd8, 0xe8, 0x90, 0xce, 0x34, 251 | 0x3a, 0x49, 0x0b, 0x7c, 0x0c, 0xdd, 0x4f, 0x59, 0xb7, 0x8a, 0x26, 0xe4, 0xd7, 0x70, 0x2a, 0xe9, 252 | 0x79, 0x21, 0xeb, 0x5a, 0x96, 0xaf, 0xed, 0x45, 0xc2, 0x8f, 0x58, 0x32, 0x4c, 0xf7, 0x0f, 0xe2, 253 | 0x3b, 0x18, 0xef, 0x9c, 0xa9, 0xae, 0x4a, 0xc2, 0xbd, 0x95, 0x09, 0x18, 0xd0, 0x3a, 0xcf, 0xcd, 254 | 0x1c, 0xcf, 0xce, 0x71, 0x18, 0x5f, 0x41, 0xe0, 0xba, 0x1f, 0xe5, 0x52, 0xa3, 0x32, 0xb5, 0x6f, 255 | 0xb8, 0xf9, 0xa8, 0x54, 0xd1, 0x3e, 0xc9, 0xe1, 0xf4, 0x9b, 0xc1, 0xd0, 0x15, 0xf3, 0x39, 0x1c, 256 | 0xcf, 0x51, 0x3b, 0x24, 0x2e, 0xf6, 0x57, 0xd8, 0x0c, 0x0c, 0x2f, 0x0e, 0x2e, 0x37, 0xee, 0xdc, 257 | 0x30, 0xfe, 0x04, 0xc1, 0x4c, 0x61, 0xa6, 0x71, 0x3b, 0xfa, 0x70, 0x43, 0x18, 0xfe, 0x77, 0xd4, 258 | 0x3c, 0x3a, 0xee, 0xbc, 0xf4, 0xed, 0x77, 0xba, 0xfd, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xde, 0x91, 259 | 0xd3, 0x62, 0x60, 0x02, 0x00, 0x00, 260 | } 261 | -------------------------------------------------------------------------------- /grpc/customer/customer.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package customer; 3 | 4 | 5 | // The Customer service definition. 6 | service Customer { 7 | // Get all Customers with filter - A server-to-client streaming RPC. 8 | rpc GetCustomers(CustomerFilter) returns (stream CustomerRequest) {} 9 | // Create a new Customer - A simple RPC 10 | rpc CreateCustomer (CustomerRequest) returns (CustomerResponse) {} 11 | } 12 | 13 | // Request message for creating a new customer 14 | message CustomerRequest { 15 | int32 id = 1; // Unique ID number for a Customer. 16 | string name = 2; 17 | string email = 3; 18 | string phone= 4; 19 | 20 | message Address { 21 | string street = 1; 22 | string city = 2; 23 | string state = 3; 24 | string zip = 4; 25 | bool isShippingAddress = 5; 26 | } 27 | 28 | repeated Address addresses = 5; 29 | } 30 | 31 | message CustomerResponse { 32 | int32 id = 1; 33 | bool success = 2; 34 | } 35 | message CustomerFilter { 36 | string keyword = 1; 37 | } -------------------------------------------------------------------------------- /grpc/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | To compile with protoc, run the following command from root directory (grpc directory): 3 | 4 | protoc -I customer/ customer/customer.proto --go_out=plugins=grpc:customer 5 | 6 | -------------------------------------------------------------------------------- /grpc/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "strings" 7 | 8 | "golang.org/x/net/context" 9 | "google.golang.org/grpc" 10 | 11 | pb "github.com/shijuvar/go-recipes/grpc/customer" 12 | ) 13 | 14 | const ( 15 | port = ":50051" 16 | ) 17 | 18 | // server is used to implement customer.CustomerServer. 19 | type server struct { 20 | savedCustomers []*pb.CustomerRequest 21 | } 22 | 23 | // CreateCustomer creates a new Customer 24 | func (s *server) CreateCustomer(ctx context.Context, in *pb.CustomerRequest) (*pb.CustomerResponse, error) { 25 | s.savedCustomers = append(s.savedCustomers, in) 26 | return &pb.CustomerResponse{Id: in.Id, Success: true}, nil 27 | } 28 | 29 | // GetCustomers returns all customers by given filter 30 | func (s *server) GetCustomers(filter *pb.CustomerFilter, stream pb.Customer_GetCustomersServer) error { 31 | for _, customer := range s.savedCustomers { 32 | if filter.Keyword != "" { 33 | if !strings.Contains(customer.Name, filter.Keyword) { 34 | continue 35 | } 36 | } 37 | if err := stream.Send(customer); err != nil { 38 | return err 39 | } 40 | } 41 | return nil 42 | } 43 | 44 | func main() { 45 | lis, err := net.Listen("tcp", port) 46 | if err != nil { 47 | log.Fatalf("failed to listen: %v", err) 48 | } 49 | // Creates a new gRPC server 50 | s := grpc.NewServer() 51 | pb.RegisterCustomerServer(s, &server{}) 52 | s.Serve(lis) 53 | } 54 | --------------------------------------------------------------------------------