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

Welcome | Message

` fmt.Fprintf(w, html) } func main() { mux := http.NewServeMux() mux.Handle("/", &indexHandler{}) thWelcome := &textHandler{"Welcome to Go Web Programming"} mux.Handle("/welcome", thWelcome) thMessage := &textHandler{"net/http package is used to build web apps"} mux.Handle("/message", thMessage) log.Println("Listening...") http.ListenAndServe(":8080", mux) } ================================================ FILE: ch07/defaultservemux/main.go ================================================ package main import ( "fmt" "log" "net/http" ) func index(w http.ResponseWriter, r *http.Request) { w.Header().Set( "Content-Type", "text/html", ) html := ` Hello Gopher Hello Gopher!

Welcome | Message

` fmt.Fprintf(w, html) } func welcome(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Welcome to Go Web Programming") } func message(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "net/http package is used to build web apps") } func main() { http.HandleFunc("/", index) http.HandleFunc("/welcome", welcome) http.HandleFunc("/message", message) log.Println("Listening...") http.ListenAndServe(":8080", nil) } ================================================ FILE: ch07/handlefunc/main.go ================================================ package main import ( "fmt" "log" "net/http" ) func index(w http.ResponseWriter, r *http.Request) { w.Header().Set( "Content-Type", "text/html", ) html := ` Hello Gopher Hello Gopher!

Welcome | Message

` fmt.Fprintf(w, html) } func welcome(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Welcome to Go Web Programming") } func message(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "net/http package is used to build web apps") } func main() { mux := http.NewServeMux() mux.HandleFunc("/", index) mux.HandleFunc("/welcome", welcome) mux.HandleFunc("/message", message) log.Println("Listening...") http.ListenAndServe(":8080", mux) } ================================================ FILE: ch07/handlerfunc/main.go ================================================ package main import ( "fmt" "log" "net/http" ) func textResponseHandler(resposeText string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, resposeText) }) } func index(w http.ResponseWriter, r *http.Request) { w.Header().Set( "Content-Type", "text/html", ) html := ` Hello Gopher Hello Gopher!

Welcome | Message

` fmt.Fprintf(w, html) } func welcome(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Welcome to Go Web Programming") } func message(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "net/http package is used to build web apps") } func main() { mux := http.NewServeMux() mux.Handle("/", http.HandlerFunc(index)) mux.Handle("/welcome", http.HandlerFunc(welcome)) mux.Handle("/message", http.HandlerFunc(message)) //mux.Handle("/welcome", textResponseHandler("Welcome to Go Web Programming")) //mux.Handle("/message", textResponseHandler("net/http package is used to build web apps")) log.Println("Listening...") http.ListenAndServe(":8080", mux) } ================================================ FILE: ch07/httpserver/main.go ================================================ package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello, world!") } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) } ================================================ FILE: ch07/middleware/main.go ================================================ package main import ( "fmt" "log" "net/http" "time" ) // loggingHandler is an HTTP Middleware that logs HTTP requests. func loggingHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Middleware logic before executing given Handler start := time.Now() log.Printf("Started %s %s", r.Method, r.URL.Path) next.ServeHTTP(w, r) // Middleware logic after executing given Handler log.Printf("Completed %s in %v", r.URL.Path, time.Since(start)) }) } func index(w http.ResponseWriter, r *http.Request) { w.Header().Set( "Content-Type", "text/html", ) html := ` Hello Gopher Hello Gopher!

Welcome | Message

` fmt.Fprintf(w, html) } func welcome(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Welcome to Go Web Programming") } func message(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "net/http package is used to build web apps") } func favicon(w http.ResponseWriter, r *http.Request) { } func main() { http.HandleFunc("/favicon.ico", favicon) http.Handle("/", loggingHandler(http.HandlerFunc(index))) http.Handle("/welcome", loggingHandler(http.HandlerFunc(welcome))) http.Handle("/message", loggingHandler(http.HandlerFunc(message))) log.Println("Listening...") http.ListenAndServe(":8080", nil) } ================================================ FILE: ch07/server/main.go ================================================ package main import ( "fmt" "log" "net/http" "time" ) func index(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Welcome to Go Web Programming") } func main() { http.HandleFunc("/", index) server := &http.Server{ Addr: ":8080", ReadTimeout: 60 * time.Second, WriteTimeout: 60 * time.Second, } log.Println("Listening...") server.ListenAndServe() } ================================================ FILE: ch08/calc/calc.go ================================================ // Package calc provides a simple calculator package calc import "math" // Sum returns sum of integer values func Sum(nums ...int) int { result := 0 for _, v := range nums { result += v } return result } // Average returns average of integer values // The output provides a float64 value in two decimal points func Average(nums ...int) float64 { sum := 0 for _, v := range nums { sum += v } result := float64(sum) / float64(len(nums)) pow := math.Pow(10, float64(2)) digit := pow * result round := math.Floor(digit) return round / pow } ================================================ FILE: ch08/calc/calc_test.go ================================================ package calc import ( "fmt" "testing" "time" ) // Test case for the function Sum func TestSum(t *testing.T) { input, expected := []int{7, 8, 10}, 25 result := Sum(input...) if result != expected { t.Errorf("Result: %d, Expected: %d", result, expected) } } // Test case for function Average func TestAverage(t *testing.T) { input, expected := []int{7, 8, 10}, 8.33 result := Average(input...) if result != expected { t.Errorf("Result: %f, Expected: %f", result, expected) } } // Benchmark for function Sum func BenchmarkSum(b *testing.B) { for i := 0; i < b.N; i++ { Sum(7, 8, 10) } } // Benchmark for function Average func BenchmarkAverage(b *testing.B) { for i := 0; i < b.N; i++ { Average(7, 8, 10) } } func TestLongRun(t *testing.T) { // Checks whether the short flag is provided if testing.Short() { t.Skip("Skipping test in short mode") } // Long running implementation goes here time.Sleep(5 * time.Second) } // Test case for the function Sum to be executed in parallel func TestSumInParallel(t *testing.T) { t.Parallel() // Delaying 1 second for the sake of demonstration time.Sleep(1 * time.Second) input, expected := []int{7, 8, 10}, 25 result := Sum(input...) if result != expected { t.Errorf("Result: %d, Expected: %d", result, expected) } } // Test case for the function Sum to be executed in parallel func TestAverageInParallel(t *testing.T) { t.Parallel() // Delaying 1 second for the sake of demonstration time.Sleep(2 * time.Second) input, expected := []int{7, 8, 10}, 8.33 result := Average(input...) if result != expected { t.Errorf("Result: %f, Expected: %f", result, expected) } } // Example code for function Sum func ExampleSum() { fmt.Println(Sum(7, 8, 10)) // Output: 25 } // Example code for function Average func ExampleAverage() { fmt.Println(Average(7, 8, 10)) // Output: 8.33 } ================================================ FILE: ch08/httpbdd/controllers/controllers_suite_test.go ================================================ package controllers_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "testing" ) func TestControllers(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Controllers Suite") } ================================================ FILE: ch08/httpbdd/controllers/user_controller.go ================================================ package controllers import ( "encoding/json" "log" "net/http" "github.com/shijuvar/go-recipes/ch08/httpbdd/model" ) // GetUsers serves requests for Http Get to "/users" func GetUsers(store model.UserStore) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { data := store.GetUsers() users, err := json.Marshal(data) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(users) }) } // CreateUser serves requests for Http Post to "/users" func CreateUser(store model.UserStore) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var user model.User // Decode the incoming User json err := json.NewDecoder(r.Body).Decode(&user) if err != nil { log.Fatalf("[Controllers.CreateUser]: %s\n", err) w.WriteHeader(http.StatusInternalServerError) return } // Insert User entity into User Store err = store.AddUser(user) if err != nil { if err == model.ErrorEmailExists { w.WriteHeader(http.StatusBadRequest) } else { w.WriteHeader(http.StatusInternalServerError) } return } w.WriteHeader(http.StatusCreated) }) } ================================================ FILE: ch08/httpbdd/controllers/user_controller_test.go ================================================ package controllers_test import ( "encoding/json" "net/http" "net/http/httptest" "strings" "github.com/shijuvar/go-recipes/ch08/httpbdd/controllers" "github.com/shijuvar/go-recipes/ch08/httpbdd/model" "github.com/gorilla/mux" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("UserController", func() { var r *mux.Router var w *httptest.ResponseRecorder var store *FakeUserStore BeforeEach(func() { r = mux.NewRouter() store = newFakeUserStore() }) // Specs for HTTP Get to "/users" Describe("Get list of Users", func() { Context("Get all Users from data store", func() { It("Should get list of Users", func() { r.Handle("/users", controllers.GetUsers(store)).Methods("GET") req, err := http.NewRequest("GET", "/users", nil) Expect(err).NotTo(HaveOccurred()) w = httptest.NewRecorder() r.ServeHTTP(w, req) Expect(w.Code).To(Equal(200)) var users []model.User json.Unmarshal(w.Body.Bytes(), &users) // Verifying mocked data of 2 users Expect(len(users)).To(Equal(2)) }) }) }) // Specs for HTTP Post to "/users" Describe("Post a new User", func() { Context("Provide a valid User data", func() { It("Should create a new User and get HTTP Status: 201", func() { r.Handle("/users", controllers.CreateUser(store)).Methods("POST") userJson := `{"firstname": "Alex", "lastname": "John", "email": "alex@xyz.com"}` req, err := http.NewRequest( "POST", "/users", strings.NewReader(userJson), ) Expect(err).NotTo(HaveOccurred()) w = httptest.NewRecorder() r.ServeHTTP(w, req) Expect(w.Code).To(Equal(201)) }) }) Context("Provide a User data that contains duplicate email id", func() { It("Should get HTTP Status: 400", func() { r.Handle("/users", controllers.CreateUser(store)).Methods("POST") userJson := `{"firstname": "Shiju", "lastname": "Varghese", "email": "shiju@xyz.com"}` req, err := http.NewRequest( "POST", "/users", strings.NewReader(userJson), ) Expect(err).NotTo(HaveOccurred()) w = httptest.NewRecorder() r.ServeHTTP(w, req) Expect(w.Code).To(Equal(400)) }) }) }) }) // FakeUserStore provides a mocked implementation of interface model.UserStore type FakeUserStore struct { userStore []model.User } // GetUsers returns all users func (store *FakeUserStore) GetUsers() []model.User { return store.userStore } // AddUser inserts a User func (store *FakeUserStore) AddUser(user model.User) error { // Check whether email is exists for _, u := range store.userStore { if u.Email == user.Email { return model.ErrorEmailExists } } store.userStore = append(store.userStore, user) return nil } // newFakeUserStore provides two dummy data for Users func newFakeUserStore() *FakeUserStore { store := &FakeUserStore{} store.AddUser(model.User{ FirstName: "Shiju", LastName: "Varghese", Email: "shiju@xyz.com", }) store.AddUser(model.User{ FirstName: "Irene", LastName: "Rose", Email: "irene@xyz.com", }) return store } ================================================ FILE: ch08/httpbdd/main.go ================================================ package main import ( "net/http" "github.com/gorilla/mux" "github.com/shijuvar/go-recipes/ch08/httpbdd/controllers" "github.com/shijuvar/go-recipes/ch08/httpbdd/store" ) func setUserRoutes() *mux.Router { r := mux.NewRouter() userStore := &store.MongoUserStore{} r.Handle("/users", controllers.CreateUser(userStore)).Methods("POST") r.Handle("/users", controllers.GetUsers(userStore)).Methods("GET") return r } func main() { http.ListenAndServe(":8080", setUserRoutes()) } ================================================ FILE: ch08/httpbdd/model/user.go ================================================ package model import "errors" // ErrorEmailExists is an error value for duplicate email id var ErrorEmailExists = errors.New("Email Id is exists") // User model type User struct { FirstName string `json:"firstname"` LastName string `json:"lastname"` Email string `json:"email"` } // UserStore provides a contract for Data Store for User entity type UserStore interface { GetUsers() []User AddUser(User) error } ================================================ FILE: ch08/httpbdd/store/user_store.go ================================================ package store import ( "log" "time" "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" "github.com/shijuvar/go-recipes/ch08/httpbdd/model" ) // MongoDB Session var mgoSession *mgo.Session // Create a MongoDB Session func createDBSession() { var err error mgoSession, err = mgo.DialWithInfo(&mgo.DialInfo{ Addrs: []string{"127.0.0.1"}, Timeout: 60 * time.Second, }) if err != nil { log.Fatalf("[createDbSession]: %s\n", err) } } // Initializes the MongoDB Session func init() { createDBSession() } // MongoUserStore provides persistence logic for "users" collection. type MongoUserStore struct{} // AddUser insert new User func (store *MongoUserStore) AddUser(user model.User) error { session := mgoSession.Copy() defer session.Close() userCol := session.DB("userdb").C("users") // Check whether email id is exists or not var existUser model.User err := userCol.Find(bson.M{"email": user.Email}).One(&existUser) if err != nil { if err == mgo.ErrNotFound { // Email is unique, no records found } } if (model.User{}) != existUser { // there is a user return model.ErrorEmailExists } err = userCol.Insert(user) return err } // GetUsers returns all documents from the collection. func (store *MongoUserStore) GetUsers() []model.User { session := mgoSession.Copy() defer session.Close() userCol := session.DB("userdb").C("users") var users []model.User iter := userCol.Find(nil).Iter() result := model.User{} for iter.Next(&result) { users = append(users, result) } return users } ================================================ FILE: ch08/httptest/main.go ================================================ package main import ( "encoding/json" "net/http" "github.com/gorilla/mux" ) // User model type User struct { FirstName string `json:"firstname"` LastName string `json:"lastname"` Email string `json:"email"` } // getUsers serves requests for Http Get to "/users" func getUsers(w http.ResponseWriter, r *http.Request) { data := []User{ User{ FirstName: "Shiju", LastName: "Varghese", Email: "shiju@xyz.com", }, User{ FirstName: "Irene", LastName: "Rose", Email: "irene@xyz.com", }, } users, err := json.Marshal(data) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(users) } func main() { r := mux.NewRouter() r.HandleFunc("/users", getUsers).Methods("GET") http.ListenAndServe(":8080", r) } ================================================ FILE: ch08/httptest/main_test.go ================================================ package main import ( "fmt" "net/http" "net/http/httptest" "testing" "github.com/gorilla/mux" ) // TestGetUsers test HTTP Get to "/users" using ResponseRecorder func TestGetUsers(t *testing.T) { r := mux.NewRouter() r.HandleFunc("/users", getUsers).Methods("GET") req, err := http.NewRequest("GET", "/users", nil) if err != nil { t.Error(err) } w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != 200 { t.Errorf("HTTP Status expected: 200, got: %d", w.Code) } } // TestGetUsersWithServer test HTTP Get to "/users" using Server func TestGetUsersWithServer(t *testing.T) { r := mux.NewRouter() r.HandleFunc("/users", getUsers).Methods("GET") server := httptest.NewServer(r) defer server.Close() usersURL := fmt.Sprintf("%s/users", server.URL) request, err := http.NewRequest("GET", usersURL, nil) res, err := http.DefaultClient.Do(request) if err != nil { t.Error(err) } if res.StatusCode != 200 { t.Errorf("HTTP Status expected: 200, got: %d", res.StatusCode) } } ================================================ FILE: grpc/client/main.go ================================================ package main import ( "io" "log" "golang.org/x/net/context" "google.golang.org/grpc" pb "github.com/shijuvar/go-recipes/grpc/customer" ) const ( address = "localhost:50051" ) // createCustomer calls the RPC method CreateCustomer of CustomerServer func createCustomer(client pb.CustomerClient, customer *pb.CustomerRequest) { resp, err := client.CreateCustomer(context.Background(), customer) if err != nil { log.Fatalf("Could not create Customer: %v", err) } if resp.Success { log.Printf("A new Customer has been added with id: %d", resp.Id) } } // getCustomers calls the RPC method GetCustomers of CustomerServer func getCustomers(client pb.CustomerClient, filter *pb.CustomerFilter) { // calling the streaming API stream, err := client.GetCustomers(context.Background(), filter) if err != nil { log.Fatalf("Error on get customers: %v", err) } for { // Receiving the stream of data customer, err := stream.Recv() if err == io.EOF { break } if err != nil { log.Fatalf("%v.GetCustomers(_) = _, %v", client, err) } log.Printf("Customer: %v", customer) } } func main() { // Set up a connection to the gRPC server. conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() // Creates a new CustomerClient client := pb.NewCustomerClient(conn) customer := &pb.CustomerRequest{ Id: 101, Name: "Shiju Varghese", Email: "shiju@xyz.com", Phone: "732-757-2923", Addresses: []*pb.CustomerRequest_Address{ &pb.CustomerRequest_Address{ Street: "1 Mission Street", City: "San Francisco", State: "CA", Zip: "94105", IsShippingAddress: false, }, &pb.CustomerRequest_Address{ Street: "Greenfield", City: "Kochi", State: "KL", Zip: "68356", IsShippingAddress: true, }, }, } // Create a new customer createCustomer(client, customer) customer = &pb.CustomerRequest{ Id: 102, Name: "Irene Rose", Email: "irene@xyz.com", Phone: "732-757-2924", Addresses: []*pb.CustomerRequest_Address{ &pb.CustomerRequest_Address{ Street: "1 Mission Street", City: "San Francisco", State: "CA", Zip: "94105", IsShippingAddress: true, }, }, } // Create a new customer createCustomer(client, customer) // Filter with an empty Keyword filter := &pb.CustomerFilter{Keyword: ""} getCustomers(client, filter) } ================================================ FILE: grpc/customer/customer.pb.go ================================================ // Code generated by protoc-gen-go. // source: customer.proto // DO NOT EDIT! /* Package customer is a generated protocol buffer package. It is generated from these files: customer.proto It has these top-level messages: CustomerRequest CustomerResponse CustomerFilter */ package customer import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" import ( context "golang.org/x/net/context" grpc "google.golang.org/grpc" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // Request message for creating a new customer type CustomerRequest struct { Id int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` Email string `protobuf:"bytes,3,opt,name=email" json:"email,omitempty"` Phone string `protobuf:"bytes,4,opt,name=phone" json:"phone,omitempty"` Addresses []*CustomerRequest_Address `protobuf:"bytes,5,rep,name=addresses" json:"addresses,omitempty"` } func (m *CustomerRequest) Reset() { *m = CustomerRequest{} } func (m *CustomerRequest) String() string { return proto.CompactTextString(m) } func (*CustomerRequest) ProtoMessage() {} func (*CustomerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (m *CustomerRequest) GetAddresses() []*CustomerRequest_Address { if m != nil { return m.Addresses } return nil } type CustomerRequest_Address struct { Street string `protobuf:"bytes,1,opt,name=street" json:"street,omitempty"` City string `protobuf:"bytes,2,opt,name=city" json:"city,omitempty"` State string `protobuf:"bytes,3,opt,name=state" json:"state,omitempty"` Zip string `protobuf:"bytes,4,opt,name=zip" json:"zip,omitempty"` IsShippingAddress bool `protobuf:"varint,5,opt,name=isShippingAddress" json:"isShippingAddress,omitempty"` } func (m *CustomerRequest_Address) Reset() { *m = CustomerRequest_Address{} } func (m *CustomerRequest_Address) String() string { return proto.CompactTextString(m) } func (*CustomerRequest_Address) ProtoMessage() {} func (*CustomerRequest_Address) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} } type CustomerResponse struct { Id int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` Success bool `protobuf:"varint,2,opt,name=success" json:"success,omitempty"` } func (m *CustomerResponse) Reset() { *m = CustomerResponse{} } func (m *CustomerResponse) String() string { return proto.CompactTextString(m) } func (*CustomerResponse) ProtoMessage() {} func (*CustomerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } type CustomerFilter struct { Keyword string `protobuf:"bytes,1,opt,name=keyword" json:"keyword,omitempty"` } func (m *CustomerFilter) Reset() { *m = CustomerFilter{} } func (m *CustomerFilter) String() string { return proto.CompactTextString(m) } func (*CustomerFilter) ProtoMessage() {} func (*CustomerFilter) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } func init() { proto.RegisterType((*CustomerRequest)(nil), "customer.CustomerRequest") proto.RegisterType((*CustomerRequest_Address)(nil), "customer.CustomerRequest.Address") proto.RegisterType((*CustomerResponse)(nil), "customer.CustomerResponse") proto.RegisterType((*CustomerFilter)(nil), "customer.CustomerFilter") } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion3 // Client API for Customer service type CustomerClient interface { // Get all Customers with filter - A server-to-client streaming RPC. GetCustomers(ctx context.Context, in *CustomerFilter, opts ...grpc.CallOption) (Customer_GetCustomersClient, error) // Create a new Customer - A simple RPC CreateCustomer(ctx context.Context, in *CustomerRequest, opts ...grpc.CallOption) (*CustomerResponse, error) } type customerClient struct { cc *grpc.ClientConn } func NewCustomerClient(cc *grpc.ClientConn) CustomerClient { return &customerClient{cc} } func (c *customerClient) GetCustomers(ctx context.Context, in *CustomerFilter, opts ...grpc.CallOption) (Customer_GetCustomersClient, error) { stream, err := grpc.NewClientStream(ctx, &_Customer_serviceDesc.Streams[0], c.cc, "/customer.Customer/GetCustomers", opts...) if err != nil { return nil, err } x := &customerGetCustomersClient{stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } type Customer_GetCustomersClient interface { Recv() (*CustomerRequest, error) grpc.ClientStream } type customerGetCustomersClient struct { grpc.ClientStream } func (x *customerGetCustomersClient) Recv() (*CustomerRequest, error) { m := new(CustomerRequest) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } return m, nil } func (c *customerClient) CreateCustomer(ctx context.Context, in *CustomerRequest, opts ...grpc.CallOption) (*CustomerResponse, error) { out := new(CustomerResponse) err := grpc.Invoke(ctx, "/customer.Customer/CreateCustomer", in, out, c.cc, opts...) if err != nil { return nil, err } return out, nil } // Server API for Customer service type CustomerServer interface { // Get all Customers with filter - A server-to-client streaming RPC. GetCustomers(*CustomerFilter, Customer_GetCustomersServer) error // Create a new Customer - A simple RPC CreateCustomer(context.Context, *CustomerRequest) (*CustomerResponse, error) } func RegisterCustomerServer(s *grpc.Server, srv CustomerServer) { s.RegisterService(&_Customer_serviceDesc, srv) } func _Customer_GetCustomers_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(CustomerFilter) if err := stream.RecvMsg(m); err != nil { return err } return srv.(CustomerServer).GetCustomers(m, &customerGetCustomersServer{stream}) } type Customer_GetCustomersServer interface { Send(*CustomerRequest) error grpc.ServerStream } type customerGetCustomersServer struct { grpc.ServerStream } func (x *customerGetCustomersServer) Send(m *CustomerRequest) error { return x.ServerStream.SendMsg(m) } func _Customer_CreateCustomer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CustomerRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(CustomerServer).CreateCustomer(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/customer.Customer/CreateCustomer", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(CustomerServer).CreateCustomer(ctx, req.(*CustomerRequest)) } return interceptor(ctx, in, info, handler) } var _Customer_serviceDesc = grpc.ServiceDesc{ ServiceName: "customer.Customer", HandlerType: (*CustomerServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "CreateCustomer", Handler: _Customer_CreateCustomer_Handler, }, }, Streams: []grpc.StreamDesc{ { StreamName: "GetCustomers", Handler: _Customer_GetCustomers_Handler, ServerStreams: true, }, }, Metadata: fileDescriptor0, } func init() { proto.RegisterFile("customer.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 326 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x92, 0xef, 0x4a, 0xc3, 0x30, 0x10, 0xc0, 0x97, 0x6e, 0xdd, 0x9f, 0x53, 0xea, 0x0c, 0x22, 0xb1, 0x9f, 0x6a, 0x3f, 0x15, 0x91, 0x21, 0xf3, 0xab, 0x20, 0x32, 0x70, 0xf8, 0xb5, 0x3e, 0x41, 0x6d, 0x0f, 0x17, 0xdc, 0xda, 0x9a, 0xcb, 0x90, 0xf9, 0x0a, 0xbe, 0x83, 0xcf, 0xe0, 0x23, 0x4a, 0xd2, 0x66, 0x03, 0xe7, 0xbe, 0xdd, 0xef, 0x72, 0x77, 0xf9, 0xe5, 0x08, 0x04, 0xf9, 0x9a, 0x74, 0xb5, 0x42, 0x35, 0xa9, 0x55, 0xa5, 0x2b, 0x3e, 0x74, 0x1c, 0xff, 0x78, 0x70, 0x32, 0x6b, 0x21, 0xc5, 0xf7, 0x35, 0x92, 0xe6, 0x01, 0x78, 0xb2, 0x10, 0x2c, 0x62, 0x89, 0x9f, 0x7a, 0xb2, 0xe0, 0x1c, 0x7a, 0x65, 0xb6, 0x42, 0xe1, 0x45, 0x2c, 0x19, 0xa5, 0x36, 0xe6, 0x67, 0xe0, 0xe3, 0x2a, 0x93, 0x4b, 0xd1, 0xb5, 0xc9, 0x06, 0x4c, 0xb6, 0x5e, 0x54, 0x25, 0x8a, 0x5e, 0x93, 0xb5, 0xc0, 0xef, 0x61, 0x94, 0x15, 0x85, 0x42, 0x22, 0x24, 0xe1, 0x47, 0xdd, 0xe4, 0x68, 0x7a, 0x39, 0xd9, 0x1a, 0xfd, 0xb9, 0x7d, 0xf2, 0xd0, 0x94, 0xa6, 0xbb, 0x9e, 0xf0, 0x8b, 0xc1, 0xa0, 0x4d, 0xf3, 0x73, 0xe8, 0x93, 0x56, 0x88, 0xda, 0x0a, 0x8e, 0xd2, 0x96, 0x8c, 0x64, 0x2e, 0xf5, 0xc6, 0x49, 0x9a, 0xd8, 0xe8, 0x90, 0xce, 0x34, 0x3a, 0x49, 0x0b, 0x7c, 0x0c, 0xdd, 0x4f, 0x59, 0xb7, 0x8a, 0x26, 0xe4, 0xd7, 0x70, 0x2a, 0xe9, 0x79, 0x21, 0xeb, 0x5a, 0x96, 0xaf, 0xed, 0x45, 0xc2, 0x8f, 0x58, 0x32, 0x4c, 0xf7, 0x0f, 0xe2, 0x3b, 0x18, 0xef, 0x9c, 0xa9, 0xae, 0x4a, 0xc2, 0xbd, 0x95, 0x09, 0x18, 0xd0, 0x3a, 0xcf, 0xcd, 0x1c, 0xcf, 0xce, 0x71, 0x18, 0x5f, 0x41, 0xe0, 0xba, 0x1f, 0xe5, 0x52, 0xa3, 0x32, 0xb5, 0x6f, 0xb8, 0xf9, 0xa8, 0x54, 0xd1, 0x3e, 0xc9, 0xe1, 0xf4, 0x9b, 0xc1, 0xd0, 0x15, 0xf3, 0x39, 0x1c, 0xcf, 0x51, 0x3b, 0x24, 0x2e, 0xf6, 0x57, 0xd8, 0x0c, 0x0c, 0x2f, 0x0e, 0x2e, 0x37, 0xee, 0xdc, 0x30, 0xfe, 0x04, 0xc1, 0x4c, 0x61, 0xa6, 0x71, 0x3b, 0xfa, 0x70, 0x43, 0x18, 0xfe, 0x77, 0xd4, 0x3c, 0x3a, 0xee, 0xbc, 0xf4, 0xed, 0x77, 0xba, 0xfd, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xde, 0x91, 0xd3, 0x62, 0x60, 0x02, 0x00, 0x00, } ================================================ FILE: grpc/customer/customer.proto ================================================ syntax = "proto3"; package customer; // The Customer service definition. service Customer { // Get all Customers with filter - A server-to-client streaming RPC. rpc GetCustomers(CustomerFilter) returns (stream CustomerRequest) {} // Create a new Customer - A simple RPC rpc CreateCustomer (CustomerRequest) returns (CustomerResponse) {} } // Request message for creating a new customer message CustomerRequest { int32 id = 1; // Unique ID number for a Customer. string name = 2; string email = 3; string phone= 4; message Address { string street = 1; string city = 2; string state = 3; string zip = 4; bool isShippingAddress = 5; } repeated Address addresses = 5; } message CustomerResponse { int32 id = 1; bool success = 2; } message CustomerFilter { string keyword = 1; } ================================================ FILE: grpc/server/main.go ================================================ package main import ( "log" "net" "strings" "golang.org/x/net/context" "google.golang.org/grpc" pb "github.com/shijuvar/go-recipes/grpc/customer" ) const ( port = ":50051" ) // server is used to implement customer.CustomerServer. type server struct { savedCustomers []*pb.CustomerRequest } // CreateCustomer creates a new Customer func (s *server) CreateCustomer(ctx context.Context, in *pb.CustomerRequest) (*pb.CustomerResponse, error) { s.savedCustomers = append(s.savedCustomers, in) return &pb.CustomerResponse{Id: in.Id, Success: true}, nil } // GetCustomers returns all customers by given filter func (s *server) GetCustomers(filter *pb.CustomerFilter, stream pb.Customer_GetCustomersServer) error { for _, customer := range s.savedCustomers { if filter.Keyword != "" { if !strings.Contains(customer.Name, filter.Keyword) { continue } } if err := stream.Send(customer); err != nil { return err } } return nil } func main() { lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v", err) } // Creates a new gRPC server s := grpc.NewServer() pb.RegisterCustomerServer(s, &server{}) s.Serve(lis) }