├── .gitignore
├── Chapter08
├── cassandra.dml
├── gorm_associations.go
├── gorm_simple.go
├── cassandra.go
└── redis.go
├── Chapter04
├── bubble_sort.go
└── quick_sort.go
├── Chapter07
├── graphql.md
├── api_hello_word.go
├── apis.md
├── go_kit.go
├── hotels_gins.go
└── graphql.go
├── Chapter01, and 02
├── pigeon
│ └── pigeon.go
├── visibility.go
├── embed.go
├── methods.go
└── interface.go
├── Chapter11
└── cicd_example.go
├── Chapter05
├── rpc_client.go
└── rpc_server.go
├── Chapter12
└── generics
│ ├── list
│ ├── list-unit.go
│ └── template.go
│ └── main.go
├── Chapter06
├── readme.md
├── nsq_prod.go
├── req_reply.go
├── fan_in_select.go
├── pipes_filter.go
├── fan_in.go
├── kafka_sync_producer.go
├── nsq_cons.go
├── fan_out.go
├── kakfka_consumer.go
├── nsq_async_cons.go
└── kakfka_async_producer.go
├── Chapter03
├── proxy.go
├── decorator.go
├── singleton.go
├── template.go
├── adaptor.go
├── composite.go
├── strategy.go
├── observer.go
├── visitor.go
├── state.go
├── mememto.go
├── bridge.go
├── abstract_factory.go
├── mediator.go
├── multiple_interfaces.go
├── creational_builder_factory.go
└── command.go
├── Chapter10
├── reservation_time.go
├── readme.md
├── seller_proxy.go
├── main.go
└── models.go
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/*
2 |
--------------------------------------------------------------------------------
/Chapter08/cassandra.dml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CREATE KEYSPACE roster WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 1};
4 |
5 | CREATE TABLE employees (
6 | id UUID,
7 | firstname varchar,
8 | lastname varchar,
9 | age int,
10 | PRIMARY KEY(id)
11 | );
--------------------------------------------------------------------------------
/Chapter04/bubble_sort.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func bubbleSort(array []int) {
4 | swapped := true;
5 | for swapped {
6 | swapped = false
7 | for i := 0; i < len(array) - 1; i++ {
8 | if array[i + 1] < array[i] {
9 | array[i + 1], array[i ] = array[i], array[i + 1]
10 | swapped = true
11 | }
12 | }
13 | }
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/Chapter07/graphql.md:
--------------------------------------------------------------------------------
1 | ## GraphQL Samples
2 | ### This file documents graphql requests for graphql.go
3 |
4 | #Create
5 | curl -g 'http://localhost:8080/graphql?query=mutation+_{createHotel(displayName:"HotelX",city:"NY",noRooms:300,starRating:5){id}}'
6 |
7 | #Get
8 | curl -g 'http://localhost:8080/graphql?query={hotel(id:"XVlBzgba"){displayName,city,noRooms,starRating}}'
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Chapter01, and 02/pigeon/pigeon.go:
--------------------------------------------------------------------------------
1 | // Package pigeon is used to demonstrate visibily and scope for packages
2 | package pigeon
3 |
4 |
5 | type Pigeon struct {
6 | Name string
7 | featherLength int
8 | }
9 |
10 | func (p *Pigeon) GetFeatherLength() int {
11 | return p.featherLength
12 | }
13 |
14 |
15 | func (p *Pigeon) SetFeatherLength(length int) {
16 | p.featherLength = length
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter11/cicd_example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/gin-gonic/gin"
6 | )
7 |
8 | func main() {
9 | fmt.Println("starting application..")
10 |
11 | // setup and route
12 | r := gin.Default()
13 | r.GET("/health", func(c *gin.Context) {
14 | c.JSON(200, gin.H{
15 | "status": "ok",
16 | })
17 | })
18 |
19 | // listen and serve on 0.0.0.0:9000
20 | r.Run(":9000")
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/Chapter07/api_hello_word.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | )
8 |
9 | func main() {
10 | // setup router
11 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
12 | log.Println("path", r.URL.Path)
13 | fmt.Fprintf(w, "pong! on %s\n", r.URL.Path)
14 | })
15 |
16 | // listen and serve
17 | err := http.ListenAndServe(":9090", nil)
18 | if err != nil {
19 | log.Fatal("ListenAndServe: ", err)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Chapter01, and 02/visibility.go:
--------------------------------------------------------------------------------
1 | // This file demonstrates package visibility along with the pigeon package
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "./pigeon"
7 | )
8 |
9 |
10 | func main() {
11 |
12 | //p := pigeon.Pigeon{"Tweety", 10} - This will not compile
13 | p := pigeon.Pigeon{Name :"Tweety", }
14 | p.SetFeatherLength(10)
15 |
16 | fmt.Println(p.Name)
17 | fmt.Println(p.GetFeatherLength())
18 | //fmt.Println(p.featherLength) - This will not compile
19 |
20 | }
--------------------------------------------------------------------------------
/Chapter05/rpc_client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "net/rpc"
6 | )
7 |
8 | type Args struct {
9 | A, B int
10 | }
11 |
12 |
13 | func main() {
14 | serverAddress := "127.0.0.1" //locahost
15 |
16 | client, err := rpc.DialHTTP("tcp", serverAddress+":1234")
17 | if err != nil {
18 | log.Fatal("dialing:", err)
19 | }
20 |
21 | // synchronous rpc
22 | args := Args{3, 4}
23 | var reply int
24 | client.Call("MuliplyService.Do", args, &reply)
25 | log.Printf(" %d*%d=%d", args.A, args.B, reply)
26 | }
27 |
--------------------------------------------------------------------------------
/Chapter01, and 02/embed.go:
--------------------------------------------------------------------------------
1 | // This file demonstrates struct embeddeding in Go
2 | package main
3 |
4 | import (
5 | "fmt"
6 | )
7 |
8 | // Bird is a sample 'super class' to demonstrate 'inheritance' via embedding
9 | type Bird struct {
10 | featherLength int
11 | classification string
12 | }
13 |
14 | // Pigeon is the derived struct
15 | type Pigeon struct {
16 | Bird
17 | featherLength float64
18 | Name string
19 | }
20 |
21 | func main() {
22 | p := Pigeon{Name :"Tweety", }
23 | p.featherLength = 3.14
24 |
25 | fmt.Println(p)
26 | }
--------------------------------------------------------------------------------
/Chapter12/generics/list/list-unit.go:
--------------------------------------------------------------------------------
1 | // This file was automatically generated by genny.
2 | // Any changes will be lost if this file is regenerated.
3 | // see https://github.com/cheekybits/genny
4 |
5 | package list
6 |
7 | type UintList struct {
8 | list []uint
9 | }
10 |
11 | func NewUintList() *UintList {
12 | return &UintList{list: []uint{}}
13 | }
14 |
15 | func (l *UintList) Add(v uint) {
16 | l.list = append(l.list, v)
17 | }
18 |
19 | func (l *UintList) Get() uint {
20 | r := l.list[0]
21 | l.list = l.list[1:]
22 | return r
23 | }
24 |
--------------------------------------------------------------------------------
/Chapter07/apis.md:
--------------------------------------------------------------------------------
1 | ## Rest Samples
2 | ### This file documents requests for various resources for hotel_gins.go
3 |
4 | #create
5 | curl -d '{"id":"xyz", "display_name":"HotelXyz", "star_rating":4, "no_rooms": 150}' -H "Content-Type: application/json" -X POST 127.0.0.1:8080/v1/hotels
6 |
7 | #get
8 | curl 127.0.0.1:8080/v1/hotels/xyz
9 |
10 | #get all
11 | curl 127.0.0.1:8080/v1/hotels
12 |
13 | #update
14 | curl -d '{"id":"xyz", "star_rating":5}' -H "Content-Type: application/json" -X PUT 127.0.0.1:8080/v1/hotels/xyz
15 |
16 |
17 | #delete
18 | curl -X DELETE 127.0.0.1:8080/v1/hotels/xyz
--------------------------------------------------------------------------------
/Chapter12/generics/main.go:
--------------------------------------------------------------------------------
1 | // This code demonstrates how we can use generics in GO using code generators
2 | // Here we use genny as the code generator
3 | package main
4 |
5 | import (
6 | "./list"
7 | "fmt"
8 | )
9 |
10 | // To install genny do 'go get github.com/cheekybits/genny'
11 | // For more information refer : https://github.com/cheekybits/genny
12 |
13 | func main() {
14 | // Create a unit List
15 | // Note that this struct and related methods are autogenerated
16 | ul := list.NewUintList()
17 |
18 | // insert 1
19 | ul.Add(1)
20 |
21 | // ensure we get it back!
22 | fmt.Println(ul.Get())
23 | }
24 |
--------------------------------------------------------------------------------
/Chapter05/rpc_server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "net/http"
6 | "net/rpc"
7 | "net"
8 |
9 | )
10 |
11 | type Args struct {
12 | A, B int
13 | }
14 |
15 | type MuliplyService struct{}
16 |
17 | func (t *MuliplyService) Do(args *Args, reply *int) error {
18 | log.Println("inside MuliplyService")
19 | *reply = args.A * args.B
20 | return nil
21 | }
22 |
23 | func main() {
24 | service := new(MuliplyService)
25 | rpc.Register(service)
26 | rpc.HandleHTTP()
27 | l, e := net.Listen("tcp", ":1234")
28 | if e != nil {
29 | log.Fatal("listen error:", e)
30 | }
31 | http.Serve(l, nil)
32 | }
33 |
--------------------------------------------------------------------------------
/Chapter06/readme.md:
--------------------------------------------------------------------------------
1 | ## Messaging
2 | ### This package contains the code shown in the messaging chapter (6).
3 |
4 | Note : each file is an independent go program, which can be run via 'go run'
5 |
6 | ### Kafka
7 | The code expects Kafka to be running lon localhost:9092
8 | You can do install Kafka on a mac using
9 | ```
10 | brew cask install java
11 | brew install kafka
12 | ```
13 | and then start is using
14 | ```
15 | zkServer start
16 | kafka-server-start.sh /usr/local/etc/kafka/server.properties
17 | ```
18 |
19 | ### nsq
20 | To install and run nsq please see [nsq quick start](https://nsq.io/overview/quick_start.html)
21 |
--------------------------------------------------------------------------------
/Chapter03/proxy.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 |
8 | // Proxy
9 | type HotelBoutiqueProxy struct {
10 | subject *HotelBoutique
11 | }
12 |
13 | func (p *HotelBoutiqueProxy) Book() {
14 | if p.subject == nil {
15 | p.subject = new(HotelBoutique)
16 | }
17 | fmt.Println("Proxy Delegating Booking call")
18 |
19 | // The API call will happen here
20 | // For example sake a simple delegation is implemented
21 | p.subject.Book()
22 | }
23 |
24 | // Dummy Subject
25 | type HotelBoutique struct{}
26 |
27 | func (s *HotelBoutique) Book() {
28 | fmt.Println("Booking done on external site")
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/Chapter10/reservation_time.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | "time"
7 | )
8 |
9 | const reservationDateFormat = "2006-01-02"
10 |
11 | type ReservationTime time.Time
12 |
13 | func (t *ReservationTime) UnmarshalJSON(bytes []byte) error {
14 | rawT, err := time.Parse(reservationDateFormat, strings.Replace(
15 | string(bytes),
16 | "\"",
17 | "",
18 | -1,
19 | ))
20 |
21 | if err != nil {
22 | return err
23 | }
24 |
25 | *t = ReservationTime(rawT)
26 |
27 | return nil
28 | }
29 |
30 | func (t *ReservationTime) MarshalJSON() ([]byte, error) {
31 | buf := fmt.Sprintf("\"%s\"", time.Time(*t).Format(reservationDateFormat))
32 | return []byte(buf), nil
33 | }
34 |
--------------------------------------------------------------------------------
/Chapter01, and 02/methods.go:
--------------------------------------------------------------------------------
1 | // This file demonstrates methods and pointer v/s value receiver
2 | package main
3 |
4 | import (
5 | "fmt"
6 | )
7 |
8 | type Person struct {
9 | Name string
10 | Age int
11 | }
12 |
13 | // Grow method has a pointer receiver. This is Pass-By-Reference
14 | func (p *Person) Grow() {
15 | p.Age++
16 | }
17 |
18 | // DoesNotGrow method has a value receiver. This is Pass-By-Value. Age will nto be modified here
19 | func (p Person) DoesNotGrow() {
20 | p.Age++
21 | }
22 |
23 | func main() {
24 | p := Person{"JY", 10}
25 | p.Grow()
26 | fmt.Println(p.Age)
27 |
28 | ptr := &p
29 | ptr.DoesNotGrow()
30 | fmt.Println(p.Age)
31 | }
32 |
33 | // will print
34 | // 11
35 | // 11
36 |
--------------------------------------------------------------------------------
/Chapter03/decorator.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "time"
6 | "math"
7 | )
8 |
9 | type Function func(float64) float64
10 |
11 | // This decorate profiles the execution time of function fn
12 | func ProfileDecorator(fn Function) Function {
13 | return func(params float64) float64 {
14 | start := time.Now()
15 | result := fn(params)
16 | elapsed := time.Now().Sub(start)
17 | fmt.Println("Funtion completed with time : ", elapsed)
18 | return result
19 | }
20 | }
21 |
22 |
23 | func SquareRoot(n float64) float64 {
24 | return math.Sqrt(n)
25 | }
26 |
27 | func main() {
28 |
29 | decoratedSqaureRoot := ProfileDecorator(SquareRoot)
30 | fmt.Println(decoratedSqaureRoot(16))
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/Chapter03/singleton.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | )
7 |
8 | type MyClass struct {
9 | attrib string
10 |
11 | }
12 |
13 | func (c* MyClass ) SetAttrib(val string) {
14 | c.attrib = val
15 | }
16 |
17 | func (c* MyClass ) GetAttrib() string {
18 | return c.attrib
19 | }
20 |
21 | var (
22 | once sync.Once
23 | instance *MyClass
24 | )
25 |
26 | func GetMyClass() *MyClass {
27 | once.Do(func() {
28 | instance = &MyClass{"first"}
29 | })
30 | return instance
31 | }
32 |
33 |
34 | func main() {
35 | a := GetMyClass()
36 | a.SetAttrib("second")
37 | fmt.Println(a.GetAttrib()) // will print second
38 | b := GetMyClass()
39 | fmt.Println(b.GetAttrib()) // will also print second
40 | }
--------------------------------------------------------------------------------
/Chapter04/quick_sort.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "math/rand"
4 |
5 |
6 | func quickSort(array []int) []int {
7 | if len(array) <= 1 {
8 | return array
9 | }
10 |
11 | left, right := 0, len(array) - 1
12 |
13 | // Pick a pivot randomly and move it to the end
14 | pivot:= rand.Int() % len(array)
15 | array[pivot], array[right] = array[right], array[pivot]
16 |
17 | // Partition
18 | for i := range array {
19 | if array[i] < array[right] {
20 | array[i], array[left] = array[left], array[i]
21 | left++
22 | }
23 | }
24 |
25 | // Put the pivot in place
26 | array[left], array[right] = array[right], array[left]
27 |
28 | // Recurse
29 | quickSort(array[:left])
30 | quickSort(array[left + 1:])
31 |
32 | return array
33 | }
34 |
--------------------------------------------------------------------------------
/Chapter03/template.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 |
8 |
9 | // The 'abstract' MasterAlgorithm
10 | type MasterAlgorithm struct {
11 | template Template
12 | }
13 |
14 | func (c *MasterAlgorithm) TemplateMethod() {
15 | // orchestrate the steps
16 | c.template.Step1()
17 | c.template.Step2()
18 | }
19 |
20 |
21 | // The steps which can be specialized
22 | type Template interface {
23 | Step1()
24 | Step2()
25 | }
26 |
27 |
28 | // Variant A
29 | type VariantA struct{}
30 | func (c *VariantA) Step1() {
31 | fmt.Println("VariantA step 1")
32 | }
33 | func (c *VariantA) Step2() {
34 | fmt.Println("VariantA step 2")
35 | }
36 |
37 | func main() {
38 | masterAlgorithm := MasterAlgorithm{new(VariantA)}
39 | masterAlgorithm.TemplateMethod()
40 | }
--------------------------------------------------------------------------------
/Chapter06/nsq_prod.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/nsqio/go-nsq"
5 | "log"
6 | )
7 |
8 | func main() {
9 |
10 | // Connect
11 | pCfg := nsq.NewConfig()
12 | producer, err := nsq.NewProducer("127.0.0.1:4150", pCfg)
13 | if err != nil {
14 | log.Fatalf("failed creating producer %s", err)
15 | }
16 |
17 | // Publish Async
18 | destinationTopic := "my_topic"
19 | responseChan := make(chan *nsq.ProducerTransaction)
20 | err = producer.PublishAsync(destinationTopic, []byte("a_message"), responseChan, "some_args")
21 |
22 | // Check for status
23 | // Done here inline just for showcase
24 | status := <-responseChan
25 | if status.Error != nil {
26 | log.Printf("Error received %s \n", status.Error.Error())
27 | } else {
28 | log.Printf("Success Arg received : %s \n", status.Args[0].(string)) // should be some_args
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/Chapter03/adaptor.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // Adaptee is the existing structure - something we need to use
8 | type Adaptee struct{}
9 |
10 | func (a *Adaptee) ExistingMethod() {
11 | fmt.Println("using existing method")
12 | }
13 |
14 | // Adapter is the structure we use to glue things together
15 | type Adapter struct {
16 | adaptee *Adaptee
17 | }
18 |
19 | func NewAdapter() *Adapter {
20 | return &Adapter{new(Adaptee)}
21 | }
22 |
23 | // ExpectedMethod is the method clients in current code are using. This honors the expected interface and fulfils it
24 | // using the Adaptee's method
25 | func (a *Adapter) ExpectedMethod() {
26 | fmt.Println("doing some work")
27 | a.adaptee.ExistingMethod()
28 | }
29 |
30 | // The code below demonstrates usage of the design pattern
31 | //
32 | func main() {
33 | adaptor := NewAdapter()
34 | adaptor.ExpectedMethod()
35 | }
--------------------------------------------------------------------------------
/Chapter03/composite.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // InterfaceX is the component interface
8 | type InterfaceX interface {
9 | MethodA()
10 | AddChild(InterfaceX)
11 | }
12 |
13 | type Composite struct{
14 | children []InterfaceX
15 | }
16 |
17 | func (c *Composite) MethodA() {
18 | if len(c.children) == 0 {
19 | fmt.Println("I'm a leaf ")
20 | return
21 | }
22 |
23 | // if there are children then the component is a composite
24 | fmt.Println("I'm a composite ")
25 | for _, child := range c.children {
26 | child.MethodA()
27 | }
28 | }
29 |
30 | func (c *Composite) AddChild(child InterfaceX) {
31 | c.children = append(c.children, child)
32 | }
33 |
34 |
35 |
36 |
37 | func main() {
38 | var parent InterfaceX
39 |
40 | parent = &Composite{}
41 | parent.MethodA() // still a leaf
42 |
43 | var child Composite
44 | parent.AddChild(&child)
45 | parent.MethodA() // one composite, one leaf
46 |
47 |
48 | }
--------------------------------------------------------------------------------
/Chapter06/req_reply.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "time"
6 | )
7 |
8 | type Request struct {
9 | someArg string
10 | replyTo chan<- Response
11 | }
12 |
13 | type Response struct {
14 | reply string
15 | }
16 |
17 | func responder(c <-chan Request) {
18 | for request := range c {
19 | var resp Response
20 | resp.reply = "reply-to-" + request.someArg
21 | request.replyTo <- resp
22 | }
23 | }
24 |
25 | func requestor(c chan<- Request) {
26 | myChannel := make(chan Response)
27 | for i := 0; i < 5; i++ {
28 | c <- Request{fmt.Sprintf("message%d", i), myChannel}
29 | resp := <-myChannel
30 | fmt.Printf("request %d , response %s\n", i, resp.reply)
31 | }
32 |
33 | // cleanup after my work is done
34 | close(myChannel)
35 | }
36 |
37 | func main() {
38 |
39 | requestChannel := make(chan Request)
40 | go responder(requestChannel)
41 | go requestor(requestChannel)
42 |
43 | time.Sleep(time.Second * 10)
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/Chapter03/strategy.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "math"
6 | "sort"
7 | )
8 |
9 |
10 | type Strategy interface {
11 | FindBreadth([]int) int // the algorithm
12 | }
13 |
14 | // A O(nlgn) implementation
15 | type NaiveAlgo struct {}
16 | func (n *NaiveAlgo) FindBreadth(set []int) int {
17 | sort.Ints(set)
18 | return set[len(set)-1] - set[0]
19 | }
20 |
21 | // A O(n) implementation
22 | type FastAlgo struct {}
23 | func (n *FastAlgo) FindBreadth(set []int) int {
24 | min := math.MaxInt32
25 | max := math.MinInt64
26 |
27 | for _,x := range(set) {
28 | if x < min {
29 | min = x
30 | }
31 | if x > max {
32 | max =x
33 | }
34 | }
35 |
36 | return max - min
37 | }
38 |
39 |
40 | // The client is ignorant to the algorithm
41 | func client(s Strategy) int {
42 | a := []int { -1, 10, 3, 1}
43 | return s.FindBreadth(a)
44 | }
45 |
46 |
47 |
48 | func main() {
49 | fmt.Println(client(&NaiveAlgo{}))
50 | fmt.Println(client(&FastAlgo{}))
51 | }
--------------------------------------------------------------------------------
/Chapter06/fan_in_select.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "time"
7 | )
8 |
9 | // this combines the sources to a Fan-In channel
10 | func fanInSelect(input1, input2 <-chan string) <-chan string {
11 | out := make(chan string)
12 | go func() {
13 | for {
14 | select {
15 | case in := <-input1:
16 | out <- in
17 | case in := <-input2:
18 | out <- in
19 | }
20 | }
21 | }()
22 | return out
23 | }
24 |
25 | func main() {
26 | c := fanInSelect(emitter("Source1"), emitter("Source2"))
27 |
28 | for i := 0; i < 10; i++ {
29 | fmt.Println(<-c) // Display the output of the FanIn channel.
30 | }
31 |
32 | }
33 |
34 | // dummy function for a source
35 | func emitter(name string) <-chan string {
36 | c := make(chan string)
37 |
38 | go func() {
39 | for i := 0; ; i++ {
40 | c <- fmt.Sprintf("[%s] says %d", name, i)
41 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) // Sleep for some time
42 | }
43 | }()
44 |
45 | return c
46 | }
47 |
--------------------------------------------------------------------------------
/Chapter12/generics/list/template.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import "github.com/cheekybits/genny/generic"
4 |
5 | // The gogenerate command is used to create multiple instantiations from the generic type
6 | // NOTE the replacement of tag "Element" - It's important that all elements below start with that
7 | //go:generate genny -in=template.go -out=list-unit.go gen "Element=uint"
8 |
9 | // Element is a generic type which will be contained in the list
10 | type Element generic.Type
11 |
12 | // ElementList is the generic FIFO queue
13 | type ElementList struct {
14 | list []Element
15 | }
16 |
17 | // NewElementList creates a new list
18 | func NewElementList() *ElementList {
19 | return &ElementList{list: []Element{}}
20 | }
21 |
22 | // Add inserts an element to the end of the list
23 | func (l *ElementList) Add(v Element) {
24 | l.list = append(l.list, v)
25 | }
26 |
27 | // Get pops the element from the head of the list
28 | func (l *ElementList) Get() Element {
29 | r := l.list[0]
30 | l.list = l.list[1:]
31 | return r
32 | }
33 |
--------------------------------------------------------------------------------
/Chapter06/pipes_filter.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func emitter(till int) <-chan int {
8 | out := make(chan int)
9 | go func() {
10 | for i := 0; i < till; i++ {
11 | out <- i
12 | }
13 | close(out)
14 | }()
15 | return out
16 | }
17 |
18 | func xSquare(in <-chan int) <-chan int {
19 | out := make(chan int)
20 | go func() {
21 | for x := range in {
22 | out <- x * x
23 | }
24 | close(out) // close forward
25 | }()
26 | return out
27 | }
28 |
29 | func addC(in <-chan int, c int) <-chan int {
30 | out := make(chan int)
31 | go func() {
32 | for x := range in {
33 | out <- x + c
34 | }
35 | close(out) // close forward
36 | }()
37 | return out
38 | }
39 |
40 | func main() {
41 | // y = x*x + c
42 | out := addC(
43 | xSquare(emitter(3)),
44 | 5)
45 |
46 | for y := range out {
47 | fmt.Println(y)
48 | }
49 |
50 | // y = x*x*x*x + c
51 |
52 | out1 := addC(
53 | xSquare(xSquare(emitter(3))),
54 | 5)
55 |
56 | for y := range out1 {
57 | fmt.Println(y)
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Packt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Chapter06/fan_in.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "time"
7 | )
8 |
9 | func main() {
10 | c := fanIn(emitter("Source1"), emitter("Source2"))
11 |
12 | for i := 0; i < 10; i++ {
13 | fmt.Println(<-c) // Display the output of the FanIn channel.
14 | }
15 |
16 | }
17 |
18 | // this combines the sources to a Fan-In channel
19 | func fanIn(input1, input2 <-chan string) <-chan string {
20 | c := make(chan string) // The FanIn channel
21 |
22 | // to avoid blocking, listen to the input channels in separate goroutines
23 | go func() {
24 | for {
25 | c <- <-input1 // Write the message to the FanIn channel, Blocking Call.
26 | }
27 | }()
28 |
29 | go func() {
30 | for {
31 | c <- <-input2 // Write the message to the FanIn channel, Blocking Call.
32 | }
33 | }()
34 |
35 | return c
36 | }
37 |
38 | // dummy function for a source
39 | func emitter(name string) <-chan string {
40 | c := make(chan string)
41 |
42 | go func() {
43 | for i := 0; ; i++ {
44 | c <- fmt.Sprintf("[%s] says %d", name, i)
45 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) // Sleep for some time
46 | }
47 | }()
48 |
49 | return c
50 | }
51 |
--------------------------------------------------------------------------------
/Chapter06/kafka_sync_producer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/Shopify/sarama"
6 | )
7 |
8 | func main() {
9 |
10 | // Config
11 | config := sarama.NewConfig()
12 | config.Producer.RequiredAcks = sarama.WaitForAll
13 | config.Producer.Retry.Max = 5
14 | config.Producer.Return.Errors = true // For sync producer this needs to be true
15 | config.Producer.Return.Success = true // For sync producer this needs to be true
16 |
17 | // Connect to a Kafka broker running locally
18 | brokers := []string{"localhost:9092"}
19 | producer, err := sarama.NewSyncProducer(brokers, config)
20 | if err != nil {
21 | panic(err)
22 | }
23 |
24 | // cleanup
25 | defer func() {
26 | if err := producer.Close(); err != nil {
27 | panic(err)
28 | }
29 | }()
30 |
31 | msg := &sarama.ProducerMessage{
32 | Topic: "currentTime",
33 | Value: sarama.StringEncoder(strconv.Itoa(int(time.Now().Unix()))),
34 | }
35 |
36 | partition, offset, err := producer.SendMessage(msg)
37 | if err != nil {
38 | fmt.Printf("FAILED to publish message: %s\n", err)
39 | } else {
40 | fmt.Printf("message sent | partition(%d)/offset(%d)\n", partition, offset)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Chapter10/readme.md:
--------------------------------------------------------------------------------
1 | # Case Study - Travel Website
2 |
3 | ## Infra Needed
4 |
5 | ### kafka
6 | The code expects Kafka to be running lon localhost:9092
7 | You can do install Kafka on a mac using
8 | ```
9 | brew cask install java
10 | brew install kafka
11 | ```
12 | and then start is using
13 | ```
14 | zkServer start
15 | kafka-server-start.sh /usr/local/etc/kafka/server.properties
16 | ```
17 |
18 | ### mysql
19 | The code expects MySQL to be installed locally - it connects to root:@tcp(127.0.0.1:3306)
20 | You can install MySQL on a mac using
21 | ```
22 | brew install mysql@5.7
23 | ```
24 |
25 | Once you install mysql, you can create the schema using the following DDL
26 | ```
27 | CREATE TABLE hotel_reservations (
28 | customer_id int,
29 | payment_identifier int,
30 | sku int,
31 | room_id int,
32 | check_in datetime,
33 | check_out datetime,
34 | status int(10),
35 | id varchar(100),
36 | PRIMARY KEY (id)
37 | );
38 | ```
39 |
40 | ## API Calls
41 |
42 | # create booking api
43 | ```
44 | curl -d '{"customer_id": 999, "payment_identifier": 66, "sku":1, "room_id": 2, "check_in" : "2018-12-07" , "check_out" : "2018-12-08"}' -H "Content-Type: application/json" -X POST 127.0.0.1:8080/v1/hotels/reservation/
45 | ```
46 |
47 |
--------------------------------------------------------------------------------
/Chapter03/observer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 |
4 | import (
5 | "fmt"
6 | )
7 |
8 | // The Subject
9 | type Subject struct {
10 | observers []Observer
11 | state string
12 | }
13 |
14 | func (s *Subject) Attach(observer Observer) {
15 | s.observers = append(s.observers, observer)
16 | }
17 |
18 |
19 | func (s *Subject) SetState(newState string) {
20 | s.state = newState
21 | for _,o := range(s.observers) {
22 | o.Update()
23 | }
24 | }
25 |
26 | func (s *Subject) GetState() string {
27 | return s.state
28 | }
29 |
30 | // The Observer Interface
31 | type Observer interface {
32 | Update()
33 | }
34 |
35 | // Concrete Observer A
36 | type ConcreteObserverA struct {
37 | model *Subject
38 | viewState string
39 | }
40 |
41 | func (ca *ConcreteObserverA) Update() {
42 | ca.viewState = ca.model.GetState()
43 | fmt.Println("ConcreteObserverA : updated view state to ", ca.viewState)
44 | }
45 |
46 | func (ca *ConcreteObserverA) SetModel(s *Subject) {
47 | ca.model = s
48 | }
49 |
50 |
51 | func main() {
52 | // create Subject
53 | s := Subject{}
54 |
55 | // create concrete observer
56 | ca := &ConcreteObserverA{}
57 | ca.SetModel(&s) // set Model
58 |
59 | // Attach the observer
60 | s.Attach(ca)
61 |
62 | s.SetState("s1")
63 |
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/Chapter06/nsq_cons.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/nsqio/go-nsq"
5 | "log"
6 | "os"
7 | "os/signal"
8 | "syscall"
9 | )
10 |
11 | // MyMessageHandler is a struct which defines a method to handle messages
12 | type MyMessageHandler struct {
13 | totalMessages int
14 | }
15 |
16 | // This is the message handler callback
17 | func (h *MyMessageHandler) HandleMessage(message *nsq.Message) error {
18 | h.totalMessages++
19 | log.Printf("Message no %d received , body : %s \n", h.totalMessages, string(message.Body))
20 | return nil
21 | }
22 |
23 | func main() {
24 | // Create a consumer
25 | config := nsq.NewConfig()
26 | topicName := "my_topic"
27 | channelName := "my_chan"
28 | cons, err := nsq.NewConsumer(topicName, channelName, config)
29 | if err != nil {
30 | log.Fatal(err)
31 | }
32 |
33 | // Add the callback for messages
34 | cons.AddHandler(&MyMessageHandler{})
35 |
36 | // Connect to external NSQD
37 | err = cons.ConnectToNSQD("127.0.0.1:4150")
38 | if err != nil {
39 | log.Panic("Could not connect to NSQD")
40 | }
41 |
42 | // Wait for a signal to quit
43 | sigChan := make(chan os.Signal, 1)
44 | signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
45 | <-sigChan
46 |
47 | // Stop the consumer
48 | cons.Stop()
49 | <-cons.StopChan // wait for cleanup
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/Chapter03/visitor.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // the Node interface
8 | type Node interface {
9 | Accept(Visitor)
10 | }
11 |
12 | type ConcreteNodeX struct{}
13 | func (n ConcreteNodeX) Accept(visitor Visitor) {
14 | visitor.Visit(n)
15 | }
16 |
17 | type ConcreteNodeY struct{}
18 | func (n ConcreteNodeY) Accept(visitor Visitor) {
19 | // do something NodeY-specific before visiting
20 | fmt.Println("ConcreteNodeY being visited !")
21 | visitor.Visit(n)
22 | }
23 |
24 |
25 | // the Vistor interface
26 | type Visitor interface {
27 | Visit(Node)
28 | }
29 |
30 | // and an implementation
31 | type ConcreteVisitor struct{}
32 | func (v ConcreteVisitor) Visit(node Node) {
33 | fmt.Println("doing something concrete")
34 |
35 | // since there is no function overloading..
36 | // this is one way of checking the concrete node type
37 | switch node.(type) {
38 | case ConcreteNodeX:
39 | fmt.Println("on Node X")
40 | case ConcreteNodeY:
41 | fmt.Println("on Node Y")
42 | }
43 | }
44 |
45 |
46 | func main() {
47 | // a simple aggregate
48 | aggregate := []Node {ConcreteNodeX{}, ConcreteNodeY{},}
49 |
50 | // a vistory
51 | visitor := new(ConcreteVisitor)
52 | // iterate and visit
53 | for _, node := range(aggregate){
54 | node.Accept(visitor)
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/Chapter06/fan_out.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "time"
7 | )
8 |
9 | type Message struct {
10 | body string
11 | key int
12 | }
13 |
14 | func main() {
15 | evenPipe, oddPipe := fanOut(emitter())
16 | sink("even", evenPipe)
17 | sink("odd", oddPipe)
18 |
19 | time.Sleep(10 * time.Second)
20 |
21 | }
22 |
23 | // this combines the sources to a Fan-In channel
24 | func fanOut(input <-chan Message) (<-chan Message, <-chan Message) {
25 | even := make(chan Message) // The fan-out channels
26 | odd := make(chan Message) // The fan-out channels
27 |
28 | // spawn the fan-out loop
29 | go func() {
30 | for {
31 | msg := <-input
32 | if msg.key%2 == 0 {
33 | even <- msg
34 | } else {
35 | odd <- msg
36 | }
37 | }
38 | }()
39 |
40 | return even, odd
41 | }
42 |
43 | // dummy function for a source
44 | func emitter() <-chan Message {
45 | c := make(chan Message)
46 |
47 | go func() {
48 | for i := 0; ; i++ {
49 | c <- Message{fmt.Sprintf("Message[%d]", i), i}
50 | time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) // Sleep for some time
51 | }
52 | }()
53 |
54 | return c
55 | }
56 |
57 | func sink(name string, in <-chan Message) {
58 | go func() {
59 | for {
60 | msg := <-in
61 | fmt.Printf("[%s] says %s\n", name, msg.body)
62 | }
63 | }()
64 | }
65 |
--------------------------------------------------------------------------------
/Chapter03/state.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // The State Inteface with the polymorphic methods
8 | type State interface {
9 | Op1(*Context)
10 | Op2(*Context)
11 | }
12 |
13 | // The Context class
14 | type Context struct {
15 | state State
16 |
17 | }
18 | func (c *Context) Op1() {
19 | c.state.Op1(c)
20 | }
21 | func (c *Context) Op2() {
22 | c.state.Op2(c)
23 | }
24 | func (c *Context) SetState(state State) {
25 | c.state = state
26 | }
27 | func NewContext() *Context{
28 | c := new(Context)
29 | c.SetState(new(StateA)) // Initial State
30 | return c
31 | }
32 |
33 |
34 | // Concrete States
35 | type StateA struct{}
36 | func (s *StateA) Op1(c *Context) {
37 | fmt.Println("State A : Op1 ")
38 | }
39 | func (s *StateA) Op2(c *Context) {
40 | fmt.Println("State A : Op2 ")
41 | c.SetState(new(StateB)) // <-- State Change!
42 | }
43 |
44 | type StateB struct{}
45 | func (s *StateB) Op1(c *Context) {
46 | fmt.Println("State B : Op1 ")
47 | }
48 | func (s *StateB) Op2(c *Context) {
49 | fmt.Println("State B : Op2 ")
50 | c.SetState(new(StateA)) // <-- State Change!
51 | }
52 |
53 |
54 | func main() {
55 | context := NewContext()
56 |
57 | // state operations
58 | context.Op1()
59 | context.Op2() // <- This changes state to State 2
60 | context.Op1()
61 | context.Op2() // <- This changes state back to State 1
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/Chapter06/kakfka_consumer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | cluster "github.com/bsm/sarama-cluster"
6 | "log"
7 | "os"
8 | "os/signal"
9 | )
10 |
11 | func main() {
12 |
13 | // setup config, enable errors and notifications
14 | config := cluster.NewConfig()
15 | config.Consumer.Return.Errors = true
16 | config.Group.Return.Notifications = true
17 |
18 | // specify Broker co-ordinates and topics of interest
19 | brokers := []string{"localhost:9092"}
20 | topics := []string{"topic_a", "topic_b"}
21 |
22 | // connect, and register specifiying the consumer group name
23 | consumer, err := cluster.NewConsumer(brokers, "my-consumer-group", topics, config)
24 | if err != nil {
25 | panic(err)
26 | }
27 | defer consumer.Close()
28 |
29 | // process errors
30 | go func() {
31 | for err := range consumer.Errors() {
32 | log.Printf("Error: %s\n", err.Error())
33 | }
34 | }()
35 |
36 | // process notifications
37 | go func() {
38 | for ntf := range consumer.Notifications() {
39 | log.Printf("Rebalanced: %+v\n", ntf)
40 | }
41 | }()
42 |
43 | // process messages
44 | for msg := range consumer.Messages() {
45 | fmt.Fprintf(os.Stdout, "%s-%d-%d-%s-%s\n",
46 | msg.Topic,
47 | msg.Partition,
48 | msg.Offset,
49 | msg.Key,
50 | msg.Value) // <- Actually process message here
51 |
52 | consumer.MarkOffset(msg, "") // Commit offeset for this message
53 |
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Chapter03/mememto.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // Originator
8 | type Originator struct {
9 | state string
10 | }
11 |
12 | func (o *Originator) GetState() string {
13 | return o.state
14 | }
15 |
16 | func (o *Originator) SetState(state string) {
17 | fmt.Println("Setting state to " + state)
18 | o.state = state
19 | }
20 |
21 | func (o *Originator) GetMemento() Memento {
22 | // externalize state to Momemto objct
23 | return Memento{o.state}
24 | }
25 |
26 |
27 | func (o *Originator) Restore(memento Memento) {
28 | // restore state
29 | o.state = memento.GetState()
30 | }
31 |
32 | // Momento
33 | type Memento struct {
34 | serializedState string
35 | }
36 |
37 | func (m *Memento) GetState() string {
38 | return m.serializedState
39 | }
40 |
41 |
42 | // caretaker
43 |
44 | func Caretaker() {
45 |
46 | // assume that A is the original state of the Orginator
47 | theOriginator := Originator{"A"}
48 | theOriginator.SetState("A")
49 | fmt.Println("theOriginator state = ", theOriginator.GetState() )
50 |
51 | // before mutating, get an momemto
52 | theMomemto := theOriginator.GetMemento()
53 |
54 | // mutate to unclean
55 | theOriginator.SetState("unclean")
56 | fmt.Println("theOriginator state = ", theOriginator.GetState() )
57 |
58 | // rollback
59 | theOriginator.Restore(theMomemto)
60 | fmt.Println("RESTORED : theOriginator state = ", theOriginator.GetState() )
61 |
62 | }
63 |
64 | func main() {
65 | Caretaker()
66 | }
--------------------------------------------------------------------------------
/Chapter08/gorm_associations.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | _ "fmt"
5 | _ "github.com/go-sql-driver/mysql"
6 | "github.com/jinzhu/gorm"
7 | _ "github.com/jinzhu/gorm/dialects/mysql"
8 | )
9 |
10 | // HotelChain is an entity which represents a group which has multiple hotels
11 | type HotelChain struct {
12 | gorm.Model
13 | Name string
14 | }
15 |
16 | // Hotel desribes a single Hotel entity with attributes like name, number of rooms , etc
17 | // It has a reference ( foreign key) to a HotelChain
18 | type Hotel struct {
19 | gorm.Model
20 | Name string
21 | NoRooms uint
22 | Chain HotelChain `gorm:"foreignkey:ChainId"` // use ChainId as foreign key
23 | ChainId uint
24 | }
25 |
26 | // Create two entities with associations between them
27 | func main() {
28 | // Connect to the DB
29 | db, err := gorm.Open("mysql", "root:@tcp(127.0.0.1:3306)/users?charset=utf8&parseTime=True")
30 | if err != nil {
31 | panic("failed to connect database")
32 | }
33 | defer db.Close()
34 |
35 | // Migrate the schema
36 | db.AutoMigrate(&HotelChain{})
37 | db.AutoMigrate(&Hotel{})
38 | // Explicitly need to mention the foreign key so that DB relationship is setup correctly
39 | // This is a quirk of GORM currently
40 | db.Model(&Hotel{}).AddForeignKey("chain_id", "hotel_chains(id)", "CASCADE", "CASCADE")
41 |
42 | // Create some entities and save
43 | taj := HotelChain{Name: "Taj"}
44 | db.Save(&taj)
45 | vivanta := Hotel{Name: "Vivanta by the sea", NoRooms: 400, Chain: taj}
46 | db.Save(&vivanta)
47 |
48 | // You will see the relationship in the DB
49 | }
50 |
--------------------------------------------------------------------------------
/Chapter03/bridge.go:
--------------------------------------------------------------------------------
1 | // this file demonstrates the bridge design pattern
2 | package main
3 |
4 | import (
5 | "fmt"
6 | )
7 |
8 |
9 | // Reservation is the main abstraction
10 | //
11 | // the abstraction here is a struct not an interface , since in Go you cant have abstract structs/interfaces ,
12 | // where one can store reference to the Seller implementation
13 | type Reservation struct {
14 | // sellerRef is the reference to the implementation
15 | sellerRef Seller
16 | }
17 |
18 | func (r Reservation) Cancel() {
19 | r.sellerRef.CancelReservation(10) // charge $10 as cancellation feed
20 | }
21 |
22 | // PremiumReservation is a specialization of the interface
23 | type PremiumReservation struct {
24 | Reservation
25 | }
26 |
27 | func (r PremiumReservation) Cancel() {
28 | r.sellerRef.CancelReservation(0) // no charges
29 | }
30 |
31 | // This is the interface for all Sellers
32 | type Seller interface {
33 | CancelReservation(charge float64)
34 | }
35 |
36 | // InstitutionSeller is an specialization of the implementation
37 | type InstitutionSeller struct {}
38 |
39 | func (s InstitutionSeller) CancelReservation(charge float64) {
40 | fmt.Println("InstitutionSeller CancelReservation charge =", charge)
41 | }
42 |
43 |
44 | // SmallScaleSeller is an specialization of the implementation
45 | type SmallScaleSeller struct {}
46 |
47 | func (s SmallScaleSeller) CancelReservation(charge float64) {
48 | fmt.Println("SmallScalSeller CancelReservation charge =", charge)
49 | }
50 |
51 |
52 |
53 | func main() {
54 | res := Reservation{InstitutionSeller{}}
55 | res.Cancel()
56 | premiumRes := PremiumReservation{Reservation{SmallScaleSeller{}}}
57 | premiumRes.Cancel()
58 | }
59 |
--------------------------------------------------------------------------------
/Chapter03/abstract_factory.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // Reservation and Invoice as two generic products
8 | type Reservation interface{}
9 | type Invoice interface{}
10 |
11 | // AbstractFactory is the abstract factory which will create two products - both which need to work with each other
12 | type AbstractFactory interface {
13 | CreateReservation() Reservation
14 | CreateInvoice() Invoice
15 | }
16 |
17 | // HotelFactory implements AbstractFactory and creates products for the Hotel family of products
18 | type HotelFactory struct{}
19 |
20 | func (f HotelFactory) CreateReservation() Reservation {
21 | return new(HotelReservation)
22 | }
23 |
24 | func (f HotelFactory) CreateInvoice() Invoice {
25 | return new(HotelInvoice)
26 | }
27 |
28 |
29 | // FlightFactory implements AbstractFactory and creates products for the Flight family of products
30 | type FlightFactory struct{}
31 |
32 | func (f FlightFactory) CreateReservation() Reservation {
33 | return new(FlightReservation)
34 | }
35 |
36 | func (f FlightFactory) CreateInvoice() Invoice {
37 | return new(FlightReservation)
38 | }
39 |
40 | // GetFactory returns a factory given a config
41 | func GetFactory(vertical string) AbstractFactory {
42 | var factory AbstractFactory
43 | switch vertical {
44 | case "flight":
45 | factory = FlightFactory{}
46 | case "hotel":
47 | factory = HotelFactory{}
48 | }
49 |
50 | return factory
51 | }
52 |
53 |
54 | // The code below demonstrates usage of the factories
55 | //
56 |
57 | type HotelReservation struct{}
58 | type HotelInvoice struct{}
59 | type FlightReservation struct{}
60 | type FlightInvoice struct{}
61 |
62 | func main() {
63 | hotelFactory := GetFactory("hotel")
64 | reservation := hotelFactory.CreateReservation()
65 | invoice := hotelFactory.CreateInvoice()
66 |
67 | fmt.Printf("%T\n", reservation)
68 | fmt.Printf("%T\n", invoice)
69 |
70 | }
--------------------------------------------------------------------------------
/Chapter03/mediator.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "strconv"
5 | "fmt"
6 | )
7 |
8 | // The Mediator and Colleague interfaces
9 | type Mediator interface {
10 | createColleagues()
11 | }
12 |
13 | type Colleague interface {
14 | setMediator(mediator Mediator)
15 | }
16 |
17 |
18 | // Concrete Colleauge 1 - uses state as string
19 | type Colleague1 struct {
20 | mediator Mediator
21 | state string
22 | }
23 |
24 | func (c *Colleague1) SetMediator(mediator Mediator) {
25 | c.mediator = mediator
26 | }
27 |
28 | func (c *Colleague1) SetState(state string) {
29 | fmt.Println("Colleague1 : setting state : ", state)
30 | c.state = state
31 | }
32 |
33 | func (c *Colleague1) GetState() string {
34 | return c.state
35 | }
36 |
37 |
38 | // Concrete Colleauge 2 - uses state as int
39 | type Colleague2 struct {
40 | mediator Mediator
41 | state int
42 | }
43 |
44 | func (c *Colleague2) SetState(state int) {
45 | fmt.Println("Colleague2 : setting state : ", state)
46 | c.state = state
47 | }
48 |
49 | func (c *Colleague2) GetState() int {
50 | return c.state
51 | }
52 |
53 | func (c *Colleague2) SetMediator(mediator Mediator) {
54 | c.mediator = mediator
55 | }
56 |
57 |
58 | // Concrete Mediator
59 | type ConcreteMediator struct {
60 | c1 Colleague1
61 | c2 Colleague2
62 | }
63 |
64 | func (m *ConcreteMediator) SetColleagueC1(c1 Colleague1) {
65 | m.c1 = c1
66 | }
67 |
68 | func (m *ConcreteMediator) SetColleagueC2(c2 Colleague2) {
69 | m.c2 = c2
70 | }
71 |
72 | func (m *ConcreteMediator) SetState(s string) {
73 | m.c1.SetState(s)
74 | stateAsString, err := strconv.Atoi(s)
75 | if err == nil {
76 | m.c2.SetState(stateAsString)
77 | fmt.Println("Mediator set status for both colleagues")
78 | }
79 | }
80 |
81 | func main() {
82 | mediator := ConcreteMediator{}
83 | c1 := Colleague1{}
84 | c2 := Colleague2{}
85 |
86 | mediator.SetColleagueC1(c1)
87 | mediator.SetColleagueC2(c2)
88 |
89 | mediator.SetState("10")
90 |
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/Chapter03/multiple_interfaces.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | type Reservation interface {
8 | GetReservationDate() string
9 | CalculateCancellationFee() float64
10 | }
11 |
12 | type HotelReservation interface {
13 | Reservation
14 | ChangeType()
15 | }
16 |
17 | type FlightReservation interface {
18 | Reservation
19 | AddExtraLuggageAllowance(peices int)
20 | }
21 |
22 | type HotelReservationImpl struct {
23 | reservationDate string
24 | }
25 |
26 | func (r HotelReservationImpl) GetReservationDate() string {
27 | return r.reservationDate
28 | }
29 |
30 | func (r HotelReservationImpl) CalculateCancellationFee() float64 {
31 | return 1.0 // flat :P
32 | }
33 |
34 | type FlightReservationImpl struct {
35 | reservationDate string
36 | luggageAllowed int
37 | }
38 |
39 | func (r FlightReservationImpl) AddExtraLuggageAllowance(peices int) {
40 | r.luggageAllowed = peices
41 | }
42 |
43 | func (r FlightReservationImpl) CalculateCancellationFee() float64 {
44 | return 2.0 // flat but slight more than hotels :P
45 | }
46 |
47 | func (r FlightReservationImpl) GetReservationDate() string {
48 | // this might look repetitive, but the idea is to provide freedom for the
49 | // derived classes to flux independently of each other
50 | return r.reservationDate
51 | }
52 |
53 | type Trip struct {
54 | reservations []Reservation
55 | }
56 |
57 | func (t *Trip) CalculateCancellationFee() float64 {
58 | total := 0.0
59 |
60 | for _, r := range t.reservations {
61 | total += r.CalculateCancellationFee()
62 | }
63 |
64 | return total
65 | }
66 |
67 | func (t *Trip) AddReservation(r Reservation) {
68 | t.reservations = append(t.reservations, r)
69 | }
70 |
71 | func main() {
72 | var (
73 | h HotelReservationImpl
74 | f FlightReservationImpl
75 | t Trip
76 | )
77 |
78 | fmt.Println(f.CalculateCancellationFee())
79 | fmt.Println(h.CalculateCancellationFee())
80 |
81 | t.AddReservation(h)
82 | t.AddReservation(f)
83 | fmt.Println(t.CalculateCancellationFee())
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/Chapter08/gorm_simple.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | _ "github.com/go-sql-driver/mysql"
6 | "github.com/jinzhu/gorm"
7 | _ "github.com/jinzhu/gorm/dialects/mysql"
8 | )
9 |
10 | type User struct {
11 | gorm.Model
12 | Name string
13 | Age uint
14 | }
15 |
16 | func main() {
17 | db, err := gorm.Open("mysql", "root:@tcp(127.0.0.1:3306)/users?charset=utf8&parseTime=True")
18 | if err != nil {
19 | panic("failed to connect database")
20 | }
21 | defer db.Close()
22 |
23 | // Automigrate the schema
24 | // Here gorm uses reflection to get to the struct and field names and then maps them to table and column names using
25 | // either the tags or the described convention
26 | db.AutoMigrate(&User{})
27 |
28 | // Create an entity for the purpose of this test
29 | db.Create(&User{Name: "James Bond", Age: 40})
30 |
31 | // Read
32 | // Find an user with id 1
33 | var user User
34 | db.First(&user, 1)
35 | fmt.Println(user)
36 |
37 | // Find an user with a specific name : James Bond
38 | db.First(&user, "Name = ?", "James Bond")
39 | fmt.Println(user)
40 |
41 | // Update - update Bond's age
42 | db.Model(&user).Update("Age", 41)
43 | fmt.Println(user)
44 |
45 | // Delete - delete user
46 | db.Delete(&user)
47 |
48 | // Transactions demo
49 | // Transactions allows the application code to assume atomicity from the database over multiple DB operations
50 | createTwoUsers(db)
51 | }
52 |
53 | func createTwoUsers(db *gorm.DB) {
54 | userA := User{Name: "UserA", Age: 20}
55 | userB := User{Name: "UserB", Age: 20}
56 |
57 | // Starts a trasaction
58 | // Tx is the handle which identifies the newly started transaction
59 | tx := db.Begin()
60 | if err := tx.Create(&userA).Error; err != nil {
61 | // Rollback causes the transaction to abort and bring the database to the consistent state before the
62 | // transaction was initiated
63 | tx.Rollback()
64 | }
65 | if err := tx.Create(&userB).Error; err != nil {
66 | tx.Rollback()
67 | }
68 |
69 | // Commit the transaction
70 | tx.Commit()
71 | }
72 |
--------------------------------------------------------------------------------
/Chapter08/cassandra.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 |
7 | "github.com/gocql/gocql"
8 | )
9 |
10 | // User desscribes an employee entity.
11 | // We will be using this to demonstrate Cassandra create, read, update, and delete flows
12 | type User struct {
13 | Id gocql.UUID
14 | FirstName string
15 | LastName string
16 | Age int
17 | }
18 |
19 | func main() {
20 | // connect to the cluster
21 | cluster := gocql.NewCluster("127.0.0.1")
22 | cluster.Keyspace = "roster"
23 | session, _ := cluster.CreateSession()
24 | defer session.Close()
25 |
26 | // generate a unique id for the employee
27 | id := gocql.TimeUUID()
28 | // create the employee in memory
29 | newEmployee := User{
30 | Id: id,
31 | FirstName: "James",
32 | LastName: "Bond",
33 | Age: 45,
34 | }
35 |
36 | // insert the employee
37 | if err := session.Query("INSERT INTO employees (id, firstname, lastname, age ) VALUES (?, ?, ?, ?)",
38 | newEmployee.Id,
39 | newEmployee.FirstName,
40 | newEmployee.LastName,
41 | newEmployee.Age).Exec(); err != nil {
42 | fmt.Println("insert error")
43 | log.Fatal(err)
44 | }
45 |
46 | // Use select to get the employee we just entered
47 | var userFromDB User
48 |
49 | if err := session.Query("SELECT id, firstname, lastname, age FROM employees WHERE id=?", id).
50 | Scan(&userFromDB.Id, &userFromDB.FirstName, &userFromDB.LastName, &userFromDB.Age); err != nil {
51 | fmt.Println("select error")
52 | log.Fatal(err)
53 | }
54 | fmt.Println(userFromDB)
55 |
56 | // Update James's Bond's age
57 | if err := session.Query("UPDATE employees SET age = 46 WHERE id = ?", id).Exec(); err != nil {
58 | fmt.Println("udpate error")
59 | log.Fatal(err)
60 | }
61 |
62 | var newAge int
63 | // Select and show the change
64 | iter := session.Query("SELECT age FROM employees WHERE id = ?", id).Iter()
65 | for iter.Scan(&newAge) {
66 | fmt.Println(newAge)
67 | }
68 | if err := iter.Close(); err != nil {
69 | log.Fatal(err)
70 | }
71 |
72 | // Delete the employe
73 | if err := session.Query("DELETE FROM employees WHERE id = ?", id).Exec(); err != nil {
74 | fmt.Println("delete error")
75 | log.Fatal(err)
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Chapter07/go_kit.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "errors"
7 | "log"
8 | "net/http"
9 |
10 | "github.com/go-kit/kit/endpoint"
11 | httptransport "github.com/go-kit/kit/transport/http"
12 | )
13 |
14 | // CountVowels counts vowels in strings.
15 | type VowelsService interface {
16 | Count(context.Context, string) int
17 | }
18 |
19 | // VowelsService is a concrete implementation of VowelsService
20 | type VowelsServiceImpl struct{}
21 |
22 | var vowels = map[rune]bool{
23 | 'a': true,
24 | 'e': true,
25 | 'i': true,
26 | 'o': true,
27 | 'u': true,
28 | }
29 |
30 | func (VowelsServiceImpl) Count(_ context.Context, s string) int {
31 | count := 0
32 | for _, c := range s {
33 | if _, ok := vowels[c]; ok {
34 | count++
35 | }
36 | }
37 |
38 | return count
39 | }
40 |
41 | // ErrEmpty is returned when an input string is empty.
42 | var ErrEmpty = errors.New("empty string")
43 |
44 | // For each method, we define request and response structs
45 | type countVowelsRequest struct {
46 | Input string `json:"input"`
47 | }
48 |
49 | type countVowelsResponse struct {
50 | Result int `json:"result"`
51 | }
52 |
53 | // An endpoint represents a single RPC in the service interface
54 | func makeEndpoint(svc VowelsService) endpoint.Endpoint {
55 | return func(ctx context.Context, request interface{}) (interface{}, error) {
56 | req := request.(countVowelsRequest)
57 | result := svc.Count(ctx, req.Input)
58 | return countVowelsResponse{result}, nil
59 | }
60 | }
61 |
62 | // Transports expose the service to the network.
63 | func main() {
64 | svc := VowelsServiceImpl{}
65 |
66 | countHandler := httptransport.NewServer(
67 | makeEndpoint(svc),
68 | decodecountVowelsRequest,
69 | encodeResponse,
70 | )
71 |
72 | http.Handle("/count", countHandler)
73 | log.Fatal(http.ListenAndServe(":8080", nil))
74 | }
75 |
76 | func decodecountVowelsRequest(_ context.Context, r *http.Request) (interface{}, error) {
77 | var request countVowelsRequest
78 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
79 | return nil, err
80 | }
81 | return request, nil
82 | }
83 |
84 | func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
85 | return json.NewEncoder(w).Encode(response)
86 | }
87 |
--------------------------------------------------------------------------------
/Chapter06/nsq_async_cons.go:
--------------------------------------------------------------------------------
1 | // This file describes how we consumers can handle messages asynchronously
2 | package main
3 |
4 | import (
5 | "github.com/nsqio/go-nsq"
6 | "log"
7 | "os"
8 | "os/signal"
9 | "syscall"
10 | )
11 |
12 | // messages received on NSQ are sent on this channel for background processing
13 | var delegateChannel chan *nsq.Message
14 |
15 | type MyMessageHandler struct {}
16 | func (h *MyMessageHandler) HandleMessage(m *nsq.Message) error {
17 | // Disable auto-acknoledgement of messages.
18 | // This means the actual background consumer from the channel will ack the message.
19 | // NOTE : If for some reason there is a restart of this instance, messages will be available again after timeout
20 | m.DisableAutoResponse()
21 | log.Printf("[nsq handler] received %s\n",string(m.Body))
22 | log.Println("delegaing..\n")
23 | delegateChannel <- m
24 | return nil
25 | }
26 |
27 |
28 |
29 | func main() {
30 |
31 | delegateChannel = make(chan *nsq.Message, 50) // make a buffered channel
32 | cfg := nsq.NewConfig()
33 | cfg.MaxInFlight = 1000 //Maximum number of messages to allow in flight (concurrency knob)
34 | topicName := "my_topic"
35 | channelName := "my_async_chan"
36 | cons, err := nsq.NewConsumer(topicName, channelName, cfg)
37 | if err != nil {
38 | log.Fatalf(err.Error())
39 | }
40 |
41 | go func() {
42 | for m := range delegateChannel {
43 | err := doSomeWork(m) // some long winded tasks
44 | if err != nil {
45 | m.Requeue(-1)
46 | continue
47 | }
48 | m.Finish()
49 | }
50 | }()
51 |
52 | // the method below is an alternative to AddHandler to enable concurrent processing
53 | // the second argument is the number of goroutines to spawn for processing
54 | cons.AddConcurrentHandlers(&MyMessageHandler{}, 20)
55 |
56 | err = cons.ConnectToNSQD("127.0.0.1:4150")
57 | if err != nil {
58 | log.Fatalf(err.Error())
59 | }
60 |
61 | // Wait for a signal to quit
62 | sigChan := make(chan os.Signal, 1)
63 | signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
64 | <-sigChan
65 |
66 | // Stop the consumer
67 | cons.Stop()
68 | <-cons.StopChan // wait for cleanup
69 |
70 | }
71 |
72 | func doSomeWork(m *nsq.Message) error {
73 | // some long winded tasks
74 | log.Printf("received msg %s\n",string(m.Body))
75 | return nil
76 | }
--------------------------------------------------------------------------------
/Chapter03/creational_builder_factory.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | type Reservation interface {
8 | GetReservationDate() string
9 | SetReservationDate(string)
10 | }
11 |
12 | type HotelReservationImpl struct {
13 | reservationDate string
14 | }
15 |
16 | type FlightReservationImpl struct {
17 | reservationDate string
18 | }
19 |
20 | func (r HotelReservationImpl) GetReservationDate() string {
21 | return r.reservationDate
22 | }
23 |
24 | func (r HotelReservationImpl) SetReservationDate(date string) {
25 | r.reservationDate = date
26 | }
27 |
28 | func (r FlightReservationImpl) GetReservationDate() string {
29 | return r.reservationDate
30 | }
31 |
32 | func (r FlightReservationImpl) SetReservationDate(date string) {
33 | r.reservationDate = date
34 | }
35 |
36 | func NewReservation(vertical, reservationDate string) Reservation {
37 | switch vertical {
38 | case "flight":
39 | return FlightReservationImpl{reservationDate}
40 | case "hotel":
41 | return HotelReservationImpl{reservationDate}
42 | default:
43 | return nil
44 | }
45 |
46 | }
47 |
48 | type ReservationBuilder interface {
49 | Vertical(string) ReservationBuilder
50 | ReservationDate(string) ReservationBuilder
51 | Build() Reservation
52 | }
53 |
54 | type reservationBuilder struct {
55 | vertical string
56 | rdate string
57 | }
58 |
59 | func (r *reservationBuilder) Vertical(v string) ReservationBuilder {
60 | r.vertical = v
61 | return r
62 | }
63 |
64 | func (r *reservationBuilder) ReservationDate(date string) ReservationBuilder {
65 | r.rdate = date
66 | return r
67 | }
68 |
69 | func (r *reservationBuilder) Build() Reservation {
70 |
71 | var builtReservation Reservation
72 |
73 | switch r.vertical {
74 | case "flight":
75 | builtReservation = FlightReservationImpl{r.rdate}
76 | case "hotel":
77 | builtReservation = HotelReservationImpl{r.rdate}
78 | }
79 |
80 | return builtReservation
81 | }
82 |
83 | func NewReservationBuilder() ReservationBuilder {
84 | return &reservationBuilder{}
85 | }
86 |
87 | func main() {
88 | hotelReservation := NewReservation("hotel", "20180101")
89 | fmt.Println(hotelReservation.GetReservationDate())
90 |
91 | builder := NewReservationBuilder()
92 | r := builder.Vertical("hotel").ReservationDate("20180101").Build()
93 | fmt.Println(r.GetReservationDate())
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/Chapter01, and 02/interface.go:
--------------------------------------------------------------------------------
1 | // This file demonstrates interfaces in Go
2 | package main
3 |
4 | import (
5 | "fmt"
6 | )
7 |
8 | type LatLong struct {
9 | Lat float64
10 | Long float64
11 | }
12 |
13 | type Animal interface {
14 | GetLocation() LatLong
15 | SetLocation(LatLong)
16 | CanFly() bool
17 | Speak() string
18 | GetName() string
19 | }
20 |
21 | // The Lion Family
22 | //
23 |
24 | type Lion struct {
25 | name string
26 | maneLength int
27 | location LatLong
28 | }
29 |
30 | func (lion *Lion) GetLocation() LatLong {
31 | return lion.location
32 | }
33 |
34 | func (lion *Lion) SetLocation(loc LatLong) {
35 | lion.location = loc
36 | }
37 |
38 | func (lion *Lion) CanFly() bool {
39 | return false
40 | }
41 |
42 | func (lion *Lion) Speak() string {
43 | return "roar"
44 | }
45 |
46 | func (lion *Lion) GetManeLength() int {
47 | return lion.maneLength
48 | }
49 |
50 | func (lion *Lion) GetName() string {
51 | return lion.name
52 | }
53 |
54 | // The Pigeon Family
55 | //
56 |
57 | type Pigeon struct {
58 | name string
59 | location LatLong
60 | }
61 |
62 | func (p *Pigeon) GetLocation() LatLong {
63 | return p.location
64 | }
65 |
66 | func (p *Pigeon) SetLocation(loc LatLong) {
67 | p.location = loc
68 | }
69 |
70 | func (p *Pigeon) CanFly() bool {
71 | return false
72 | }
73 |
74 | func (p *Pigeon) Speak() string {
75 | return "hoot"
76 | }
77 |
78 | func (p *Pigeon) GetName() string {
79 | return p.name
80 | }
81 |
82 | // The symphony
83 | // makeThemSing demonstraces how client code can work with interfaces and not worry about struct specifics
84 | func makeThemSing(animals []Animal) {
85 | for _, animal := range animals {
86 | fmt.Println(animal.GetName() + " says " + animal.Speak())
87 | }
88 | }
89 |
90 | func main() {
91 | var myZoo []Animal
92 |
93 | Leo := Lion{
94 | "Leo",
95 | 10,
96 | LatLong{10.40, 11.5},
97 | }
98 | myZoo = append(myZoo, &Leo)
99 |
100 | Tweety := Pigeon{
101 | "Tweety",
102 | LatLong{10.40, 11.5},
103 | }
104 | myZoo = append(myZoo, &Tweety)
105 |
106 | makeThemSing(myZoo)
107 |
108 |
109 | var aAnimal Animal
110 |
111 | aAnimal = &Lion{
112 | "Leo",
113 | 10,
114 | LatLong{10.40, 11.5},
115 | }
116 |
117 | fmt.Println(aAnimal)
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/Chapter06/kakfka_async_producer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/Shopify/sarama"
6 | "log"
7 | "os"
8 | "os/signal"
9 | "strconv"
10 | "time"
11 | )
12 |
13 | // Toy message that we want to send
14 | type Message struct {
15 | Who string
16 | TimeAsString string
17 | }
18 |
19 | func main() {
20 |
21 | // Create configuration
22 | config := sarama.NewConfig()
23 | // The setting below indicates the level of reliability needed
24 | // Here we are saying we want all brokers in the ISR to ack
25 | config.Producer.RequiredAcks = sarama.WaitForAll
26 | // The total number of times to retry sending a message (default 3).
27 | config.Producer.Retry.Max = 5
28 |
29 | // you don’t need to give a list of all the brokers, just few seeds which will
30 | // tell the client about other brokers in the cluster
31 | brokers := []string{"localhost:9092"}
32 | asyncProducer, err := sarama.NewAsyncProducer(brokers, config)
33 | if err != nil {
34 | // Could not connect
35 | panic(err)
36 | }
37 |
38 | defer func() {
39 | if err := asyncProducer.Close(); err != nil {
40 | log.Fatalln(err)
41 | }
42 | }()
43 |
44 | // Trap SIGINT to break from the loop and clean up.
45 | signals := make(chan os.Signal, 1)
46 | signal.Notify(signals, os.Interrupt)
47 | exitProgram := make(chan struct{})
48 |
49 | // Simple while(1) look to send current time.
50 | var nPublished, nErrors int
51 | go func() {
52 | for {
53 | time.Sleep(5 * time.Second)
54 |
55 | // construct a message
56 | body := Message{
57 | Who: "aProcess",
58 | TimeAsString: strconv.Itoa(int(time.Now().Unix())),
59 | }
60 |
61 | // marshall it
62 | payload, _ := json.Marshal(body)
63 |
64 | msg := &sarama.ProducerMessage{
65 | Topic: "currentTime",
66 | Key: sarama.StringEncoder("aKey"),
67 | Value: sarama.ByteEncoder(payload),
68 | }
69 | select {
70 | case producer.Input() <- msg:
71 | nPublished++
72 | fmt.Println("Produce message")
73 | case err := <-producer.Errors():
74 | nErrors++
75 | fmt.Println("Failed to produce message:", err)
76 | case <-signals:
77 | exitProgram <- struct{}{}
78 | }
79 |
80 | log.Printf("Published: %d; Errors: %d\n", nPublished, nErrors)
81 |
82 | }
83 | }()
84 |
85 | <-exitProgram // wait here till program gets killed
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/Chapter03/command.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "errors"
6 | )
7 |
8 | // The Command - encapsulates the action to be done
9 | type Report interface {
10 | Execute() // here the action is called Execute
11 | }
12 |
13 |
14 | // The Concrete Commands
15 | type ConcreteReportA struct {
16 | // The action needs to be done on this receiver object
17 | receiver *Receiver
18 | }
19 |
20 | func (c *ConcreteReportA) Execute() {
21 | c.receiver.Action("ReportA")
22 | }
23 |
24 | type ConcreteReportB struct {
25 | receiver *Receiver
26 | }
27 |
28 | func (c *ConcreteReportB) Execute() {
29 | c.receiver.Action("ReportB")
30 | }
31 | // end of concrete commands
32 |
33 | // Receiver - ancillary objects passed to command execution
34 | // This can pass useful information for
35 | type Receiver struct{}
36 |
37 | func (r *Receiver) Action(msg string) {
38 | fmt.Println(msg)
39 | }
40 |
41 | // Invoker - this object which knows how to execute a command, and optionally
42 | // does bookkeeping about the command execution.
43 | type Invoker struct {
44 | repository []Report
45 | }
46 |
47 | func (i *Invoker) Schedule(cmd Report) {
48 | i.repository = append(i.repository, cmd)
49 | }
50 |
51 | func (i *Invoker) Run() {
52 | for _, cmd := range i.repository {
53 | cmd.Execute()
54 | }
55 | }
56 |
57 |
58 | // Chain Of Responsibilty
59 | // uses Command to represent requests as objects
60 |
61 | type ChainedReceiver struct {
62 | canHandle string
63 | next *ChainedReceiver
64 | }
65 |
66 | func (r *ChainedReceiver) SetNext(next *ChainedReceiver) {
67 | r.next = next
68 | }
69 |
70 | func (r *ChainedReceiver) Finish() error {
71 | fmt.Println(r.canHandle, " Receiver Finishing")
72 | return nil
73 | }
74 |
75 | func (r *ChainedReceiver) Handle(what string) error {
76 | // Check if this receiver can handle
77 | // this of course is a dummy check
78 | if what==r.canHandle {
79 | return r.Finish()
80 | } else if r.next != nil {
81 | return r.next.Handle(what)
82 | } else {
83 | fmt.Println("No Receiver could handle the request!")
84 | return errors.New("No Receiver to Handle")
85 | }
86 |
87 | }
88 |
89 |
90 | // The code below demonstrates usage of the design pattern
91 | //
92 |
93 | func main() {
94 | receiver := new(Receiver)
95 | ReportA := &ConcreteReportA{receiver}
96 | ReportB := &ConcreteReportB{receiver}
97 | invoker := new(Invoker)
98 | invoker.Schedule(ReportA)
99 | invoker.Run()
100 | invoker.Schedule(ReportB)
101 | invoker.Run()
102 |
103 | }
--------------------------------------------------------------------------------
/Chapter10/seller_proxy.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | cluster "github.com/bsm/sarama-cluster"
7 | "log"
8 | "os"
9 | "os/signal"
10 | )
11 |
12 | var (
13 | signals chan os.Signal
14 | )
15 |
16 | func init() {
17 |
18 | // setup config, enable errors and notifications
19 | config := cluster.NewConfig()
20 | config.Consumer.Return.Errors = true
21 | config.Group.Mode = cluster.ConsumerModePartitions
22 | config.Group.Return.Notifications = true
23 |
24 | // specify Broker co-ordinates and topics of interest
25 | // should be done from config
26 | brokers := []string{"localhost:9092"}
27 | topics := []string{"create_booking"}
28 |
29 | // trap SIGINT to trigger a shutdown.
30 | signals = make(chan os.Signal, 1)
31 | signal.Notify(signals, os.Interrupt)
32 |
33 | // connect, and register specifiying the consumer group name
34 | consumer, err := cluster.NewConsumer(brokers, "booking-service", topics, config)
35 | if err != nil {
36 | panic(err)
37 | }
38 |
39 | // process errors
40 | go func() {
41 | for err := range consumer.Errors() {
42 | log.Printf("Error: %s\n", err.Error())
43 | }
44 | }()
45 |
46 | // process notifications
47 | go func() {
48 | for ntf := range consumer.Notifications() {
49 | log.Printf("Rebalanced: %+v\n", ntf)
50 | }
51 | }()
52 |
53 | //start the listener thread
54 | go handleCreateBookingMessage(consumer)
55 | }
56 |
57 | func handleCreateBookingMessage(consumer *cluster.Consumer) {
58 |
59 | for {
60 |
61 | select {
62 | case partition, ok := <-consumer.Partitions():
63 | if !ok {
64 | panic("kafka consumer : error getting paritions..")
65 | }
66 |
67 | // start a separate goroutine to consume messages
68 | go func(pc cluster.PartitionConsumer) {
69 | for msg := range pc.Messages() {
70 |
71 | var reservationDTO HotelReservationDTO
72 | if err := json.Unmarshal(msg.Value, &reservationDTO); err != nil {
73 | fmt.Println("unmarshalling error", err)
74 | // Commit even on error to avoid poison pills
75 | consumer.MarkOffset(msg, "")
76 | continue
77 | }
78 |
79 | // TODO make actual booking with seller
80 |
81 | // update status in DB
82 | updateReservationStatus(&reservationDTO, BookingMade)
83 |
84 | fmt.Printf("processed create booking message %s-%d-%d-%s-%s\n",
85 | msg.Topic,
86 | msg.Partition,
87 | msg.Offset,
88 | msg.Key,
89 | msg.Value) // <- Actually process message here
90 |
91 | consumer.MarkOffset(msg, "") // Commit offeset for this message
92 |
93 | }
94 | }(partition)
95 | case <-signals:
96 | fmt.Println("consumer killed..")
97 | return
98 | }
99 |
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Chapter10/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "github.com/Shopify/sarama"
7 | "github.com/gin-gonic/gin"
8 | "log"
9 | "net/http"
10 | )
11 |
12 | var (
13 | kafkaProducer sarama.SyncProducer
14 | )
15 |
16 | func init() {
17 | var err error
18 |
19 | // Config
20 | config := sarama.NewConfig()
21 | config.Producer.RequiredAcks = sarama.WaitForAll
22 | config.Producer.Retry.Max = 5
23 | config.Producer.Return.Errors = true // For sync producer this needs to be true
24 | config.Producer.Return.Successes = true // For sync producer this needs to be true
25 |
26 | // Connect to a Kafka broker running locally
27 | // TODO : get the connection string from config
28 | brokers := []string{"localhost:9092"}
29 | kafkaProducer, err = sarama.NewSyncProducer(brokers, config)
30 | if err != nil {
31 | panic(err)
32 | }
33 |
34 | }
35 |
36 | func main() {
37 | router := gin.Default()
38 | v1 := router.Group("/v1/hotels/Chapter10")
39 | {
40 | v1.POST("/", createReservation)
41 |
42 | }
43 | router.Run()
44 | }
45 |
46 | type HotelReservationDTO struct {
47 | CustomerId uint `json:"customer_id" `
48 | PaymentIdentifier uint `json:"payment_identifier" `
49 | SKU uint `json:"entity_id" `
50 | RoomId uint `json:"room_id" `
51 | CheckIn ReservationTime `json:"check_in" gorm:"type:datetime"`
52 | CheckOut ReservationTime `json:"check_out" gorm:"type:datetime"`
53 | }
54 |
55 | func createReservation(c *gin.Context) {
56 | var (
57 | reservationDTO HotelReservationDTO
58 | err error
59 | )
60 |
61 | fmt.Println("In createReservation ")
62 |
63 | if err = c.ShouldBindJSON(&reservationDTO); err == nil {
64 | fmt.Printf("In createReservation : %+v\n", reservationDTO)
65 | err = persistReservation(&reservationDTO)
66 | sendMessageToPerformBooking(&reservationDTO)
67 | //return OK
68 | c.JSON(http.StatusAccepted, gin.H{"status": "created"})
69 | }
70 |
71 | if err != nil {
72 | // some params not correct
73 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
74 | }
75 |
76 | }
77 |
78 | // Send message to Booking service to enable the next stage in the workflow
79 | func sendMessageToPerformBooking(reservationDTO *HotelReservationDTO) {
80 | log.Println("sending message to kickstart booking for ", reservationDTO)
81 |
82 | bytes, err := json.Marshal(reservationDTO)
83 | if err != nil {
84 | log.Println("error sending message to Kafka ", err)
85 | return
86 | }
87 |
88 | // We are not setting a message key, which means that all messages will
89 | // be distributed randomly over the different partitions.
90 | msg := &sarama.ProducerMessage{
91 | Topic: "create_booking",
92 | Value: sarama.ByteEncoder(bytes),
93 | }
94 |
95 | partition, offset, err := kafkaProducer.SendMessage(msg)
96 | if err != nil {
97 | fmt.Printf("FAILED to publish message: %s\n", err)
98 | } else {
99 | fmt.Printf("message sent | partition(%d)/offset(%d)\n", partition, offset)
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Chapter07/hotels_gins.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | )
8 |
9 | func main() {
10 | router := gin.Default()
11 | v1 := router.Group("/v1/hotels")
12 | {
13 | v1.POST("/", createHotel)
14 | v1.GET("/", getAllHotels)
15 | v1.GET("/:id", getHotel)
16 | v1.PUT("/:id", updateHotel)
17 | v1.DELETE("/:id", deleteHotel)
18 | }
19 | router.Run()
20 | }
21 |
22 | type Hotel struct {
23 | Id string `json:"id" binding:"required"`
24 | DisplayName string `json:"display_name" `
25 | StarRating int `json:"star_rating" `
26 | NoRooms int `json:"no_rooms" `
27 | Links []Link `json:"links"`
28 | }
29 |
30 | type Link struct {
31 | Href string `json:"href"`
32 | Rel string `json:"rel"`
33 | Type string `json:"type"`
34 | }
35 |
36 | var (
37 | repository map[string]*Hotel
38 | )
39 |
40 | func init() {
41 | repository = make(map[string]*Hotel)
42 | }
43 |
44 | func (h *Hotel) generateHateosLinks(url string) {
45 | // Book url
46 | postLink := Link{
47 | Href: url + "book",
48 | Rel: "book",
49 | Type: "POST",
50 | }
51 |
52 | h.Links = append(h.Links, postLink)
53 | }
54 |
55 | func createHotel(c *gin.Context) {
56 | var hotel Hotel
57 | if err := c.ShouldBindJSON(&hotel); err == nil {
58 | // add HATEOS links
59 | hotel.generateHateosLinks(c.Request.URL.String())
60 | // add hotel to repository
61 | repository[hotel.Id] = &hotel
62 |
63 | //return OK
64 | c.JSON(http.StatusAccepted, gin.H{"status": "created"})
65 | } else {
66 | // some params not correct
67 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
68 | }
69 | }
70 |
71 | func getHotel(c *gin.Context) {
72 | // get ID from path param
73 | hotelId := c.Param("id")
74 |
75 | // get hotel object from repository
76 | hotel, found := repository[hotelId]
77 | fmt.Println(hotel, found, hotelId)
78 | if !found {
79 | c.JSON(http.StatusNotFound, gin.H{"status": "hotel with id not found"})
80 | } else {
81 | c.JSON(http.StatusOK, gin.H{"result": hotel})
82 | }
83 |
84 | }
85 |
86 | func updateHotel(c *gin.Context) {
87 | // get hotel object from repository
88 | hotelId := c.Param("id")
89 | hotel, found := repository[hotelId]
90 | if !found {
91 | c.JSON(http.StatusNotFound, gin.H{"status": "hotel with id not found"})
92 | } else {
93 | //update
94 | if err := c.ShouldBindJSON(&hotel); err == nil {
95 | repository[hotel.Id] = hotel
96 |
97 | //return OK
98 | c.JSON(http.StatusOK, gin.H{"status": "ok"})
99 | } else {
100 | // some params not correct
101 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
102 | }
103 | }
104 |
105 | }
106 |
107 | func getAllHotels(c *gin.Context) {
108 | c.JSON(http.StatusOK, gin.H{"result": repository})
109 | }
110 |
111 | func deleteHotel(c *gin.Context) {
112 | hotelId := c.Param("id")
113 | _, found := repository[hotelId]
114 | if !found {
115 | c.JSON(http.StatusNotFound, gin.H{"status": "hotel with id not found"})
116 | } else {
117 | delete(repository, hotelId)
118 | //return OK
119 | c.JSON(http.StatusOK, gin.H{"status": "ok"})
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/Chapter10/models.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | _ "github.com/go-sql-driver/mysql"
6 | "github.com/jinzhu/gorm"
7 | _ "github.com/jinzhu/gorm/dialects/mysql"
8 | "time"
9 | )
10 |
11 | type Status int
12 |
13 | const (
14 | Initial Status = 0
15 | BookingMade Status = 1
16 | EmailSent Status = 2
17 | )
18 |
19 | type HotelReservation struct {
20 | //HotelReservationDetails `gorm:"embedded"`
21 | CustomerId uint `json:"customer_id" `
22 | PaymentIdentifier uint `json:"payment_identifier" `
23 | SKU uint `json:"entity_id" `
24 | RoomId uint `json:"room_id" `
25 | CheckIn time.Time `json:"check_in" gorm:"type:datetime"`
26 | CheckOut time.Time `json:"check_out" gorm:"type:datetime"`
27 | Id string
28 | Status Status
29 | }
30 |
31 | type AvailabilityThreshold struct {
32 | gorm.Model
33 | SKU uint
34 | RoomId uint
35 | Availability int
36 | }
37 |
38 | var (
39 | db *gorm.DB
40 | )
41 |
42 | func init() {
43 | var err error
44 | db, err = gorm.Open("mysql", "root:@tcp(127.0.0.1:3306)/hotels?charset=utf8&parseTime=True")
45 | if err != nil {
46 | panic("failed to connect database")
47 | }
48 |
49 | // Migrate the schema
50 | //db.AutoMigrate(&HotelReservation{})
51 | db.AutoMigrate(&AvailabilityThreshold{})
52 |
53 | // dummy thresholds
54 | db.Create(&AvailabilityThreshold{SKU: 1, RoomId: 2, Availability: 3})
55 |
56 | }
57 |
58 | // generates ID for the Chapter10 from SKU, roomid and checkin date
59 | func makeId(res *HotelReservationDTO) string {
60 | // NOTE : for uniquess, non-overlapping reservations, there should be another explicit check
61 | return fmt.Sprintf("%v#%v#%v", res.SKU, res.RoomId, res.CheckIn)
62 | }
63 |
64 | func persistReservation(res *HotelReservationDTO) error {
65 | // Note the use of tx as the database handle once you are within a transaction
66 | tx := db.Begin()
67 | defer func() {
68 | if r := recover(); r != nil {
69 | tx.Rollback()
70 | }
71 | }()
72 |
73 | if tx.Error != nil {
74 | return tx.Error
75 | }
76 |
77 | //TODO : Check that there is no overlapping Chapter10
78 |
79 | if err := tx.Create(&HotelReservation{
80 | CustomerId: res.CustomerId,
81 | PaymentIdentifier: res.PaymentIdentifier,
82 | SKU: res.SKU,
83 | RoomId: res.RoomId,
84 | CheckIn: time.Time(res.CheckIn),
85 | CheckOut: time.Time(res.CheckOut),
86 | Id: makeId(res),
87 | Status: Initial}).Error; err != nil {
88 | tx.Rollback()
89 | return err
90 | }
91 |
92 | fmt.Println("created hotel Chapter10..")
93 |
94 | // update the entry for availability threshold
95 | var threshold AvailabilityThreshold
96 | tx.Where("entity_id = ? AND room_id = ?", res.SKU, res.RoomId).First(&threshold)
97 |
98 | fmt.Printf("\nthreshold = %+v\n", threshold)
99 | tx.Model(&threshold).Where("id = ?", threshold.ID).Update("availability", threshold.Availability-1)
100 |
101 | // NOTE : availability is just a threshold for update here.
102 | // Even if availability is 0, Chapter10 is forwarded to the Seller
103 | // And availability >0 in thresholds DB is not a guarantee of Chapter10 certainity.
104 | if threshold.Availability <= 1 {
105 | // we have reached threshold
106 | sendInvaliationMessageToPriceStore(threshold.SKU, threshold.RoomId)
107 |
108 | }
109 |
110 | return tx.Commit().Error
111 | }
112 |
113 | func sendInvaliationMessageToPriceStore(eid, rid uint) {
114 | fmt.Println("sending message to invalide catalog for entity id ", eid, " room id ", rid)
115 | }
116 |
117 | func updateReservationStatus(reservationDTO *HotelReservationDTO, status Status) error {
118 | tx := db.Begin()
119 | defer func() {
120 | if r := recover(); r != nil {
121 | tx.Rollback()
122 | }
123 | }()
124 |
125 | if tx.Error != nil {
126 | return tx.Error
127 | }
128 |
129 | tx.Model(&HotelReservation{}).Where("id = ?", makeId(reservationDTO)).Update("status", status)
130 |
131 | return tx.Commit().Error
132 | }
133 |
--------------------------------------------------------------------------------
/Chapter08/redis.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/mediocregopher/radix.v2/redis"
7 | "strconv"
8 | )
9 |
10 | type Hotel struct {
11 | Id string
12 | Name string
13 | City string
14 | StarRating int
15 | Likes int
16 | }
17 |
18 | func main() {
19 | conn, err := redis.Dial("tcp", "localhost:6379")
20 | if err != nil {
21 | panic(err)
22 | }
23 | defer conn.Close()
24 |
25 | // save an object in redis
26 | taj := Hotel{"taj", "Hotel Taj", "Mumbai", 4, 0}
27 | fmt.Println(taj)
28 | setHotel(conn, &taj)
29 |
30 | // retrieve an object from redis
31 | hotel, err := getHotel(conn, "taj")
32 | fmt.Println(hotel, err)
33 |
34 | //simulate some likes
35 | // 3 likes for Taj
36 | err = incrementLikes(conn, "taj")
37 | if err != nil {
38 | fmt.Println("incrementLikes error ", err)
39 | }
40 | incrementLikes(conn, "taj")
41 | incrementLikes(conn, "taj")
42 |
43 | // some other hotels to test the sorted set
44 | leela := Hotel{"leela", "Leeka K", "Bangalore", 4, 0}
45 | setHotel(conn, &leela)
46 | // 2 likes for leeka
47 | incrementLikes(conn, "leela")
48 | incrementLikes(conn, "leela")
49 |
50 | notGood := Hotel{"notSoGood", "HotelXX", "Bangalore", 4, 0}
51 | setHotel(conn, ¬Good)
52 | // only 1 like
53 | incrementLikes(conn, "notSoGood")
54 |
55 | top3, err := top3LikedHotels(conn)
56 | fmt.Println(top3, err)
57 |
58 | }
59 |
60 | func getHotel(conn *redis.Client, id string) (*Hotel, error) {
61 | reply, err := conn.Cmd("HGETALL", "hotels:"+id).Map()
62 | if err != nil {
63 | return nil, err
64 | }
65 |
66 | h := new(Hotel)
67 | h.Id = id
68 | h.Name = reply["name"]
69 | h.City = reply["city"]
70 | if h.Likes, err = strconv.Atoi(reply["likes"]); err != nil {
71 | fmt.Println("likes err", err)
72 | return nil, err
73 | }
74 | if h.StarRating, err = strconv.Atoi(reply["rating"]); err != nil {
75 | fmt.Println("ratings err", err)
76 | return nil, err
77 | }
78 |
79 | return h, nil
80 | }
81 |
82 | func setHotel(conn *redis.Client, h *Hotel) error {
83 | resp := conn.Cmd("HMSET",
84 | "hotels:"+h.Id,
85 | "name", h.Name,
86 | "city", h.City,
87 | "likes", h.Likes,
88 | "rating", h.StarRating)
89 | if resp.Err != nil {
90 | fmt.Println("save err", resp.Err)
91 | return resp.Err
92 | }
93 |
94 | return nil
95 | }
96 |
97 | func incrementLikes(conn *redis.Client, id string) error {
98 |
99 | // Sanity check to ensure that the hotel exists!
100 | exists, err := conn.Cmd("EXISTS", "hotels:"+id).Int()
101 | if err != nil || exists == 0 {
102 | return errors.New("no such hotel")
103 | }
104 |
105 | // Use the MULTI command to inform Redis that we are starting a new
106 | // transaction.
107 | err = conn.Cmd("MULTI").Err
108 | if err != nil {
109 | return err
110 | }
111 |
112 | // Increment the number of likes for the hotel. in the album hash by 1.
113 | // Because we have initiated a MULTI command, this HINCRBY command is queued NOT executed.
114 | // We still check the reply's Err field to check if there was an error for the queing
115 | err = conn.Cmd("HINCRBY", "hotels:"+id, "likes", 1).Err
116 | if err != nil {
117 | return err
118 | }
119 |
120 | // Now we increment the leaderboard sorted set
121 | err = conn.Cmd("ZINCRBY", "likes", 1, id).Err
122 | if err != nil {
123 | return err
124 | }
125 |
126 | // Execute both commands in our transaction atomically.
127 | // EXEC returns the replies from both commands as an array
128 | err = conn.Cmd("EXEC").Err
129 | if err != nil {
130 | return err
131 | }
132 | return nil
133 | }
134 |
135 | func top3LikedHotels(conn *redis.Client) ([]string, error) {
136 | // Use the ZREVRANGE command to fetch the hotels from likes sorted set
137 | // with the highest score first
138 | // The start and stop values are zero-based indexes, so we use 0 and 2
139 | // respectively to limit the reply to the top three.
140 |
141 | reply, err := conn.Cmd("ZREVRANGE", "likes", 0, 2).List()
142 | if err != nil {
143 | return nil, err
144 | }
145 |
146 | return reply, nil
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/Chapter07/graphql.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "github.com/graphql-go/graphql"
7 | "math/rand"
8 | "net/http"
9 | )
10 |
11 | type Hotel struct {
12 | Id string `json:"id"`
13 | DisplayName string `json:"displayName"`
14 | City string `json:"city"`
15 | NoRooms int `json:"noRooms"`
16 | StarRating int `json:"starRating"`
17 | }
18 |
19 | // define custom GraphQL ObjectType `hotelType` for our Golang struct `Hotel`
20 | // Note that
21 | // - the fields map with the json tags for the fields in our struct
22 | // - the field types match the field type in our struct
23 | var hotelType = graphql.NewObject(graphql.ObjectConfig{
24 | Name: "Hotel",
25 | Fields: graphql.Fields{
26 | "id": &graphql.Field{
27 | Type: graphql.String,
28 | },
29 | "displayName": &graphql.Field{
30 | Type: graphql.String,
31 | },
32 | "city": &graphql.Field{
33 | Type: graphql.String,
34 | },
35 | "noRooms": &graphql.Field{
36 | Type: graphql.Int,
37 | },
38 | "starRating": &graphql.Field{
39 | Type: graphql.Int,
40 | },
41 | },
42 | })
43 |
44 | // define schema, with our rootQuery and rootMutation
45 | var schema, schemaErr = graphql.NewSchema(graphql.SchemaConfig{
46 | Query: rootQuery,
47 | Mutation: rootMutation,
48 | })
49 |
50 | func main() {
51 |
52 | if schemaErr != nil {
53 | panic(schemaErr)
54 | }
55 |
56 | http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) {
57 | fmt.Println("[in handler]", r.URL.Query())
58 | result := executeQuery(r.URL.Query()["query"][0], schema)
59 | json.NewEncoder(w).Encode(result)
60 | })
61 |
62 | fmt.Println("Graphql server is running on port 8080")
63 | http.ListenAndServe(":8080", nil)
64 | }
65 |
66 | func executeQuery(query string, schema graphql.Schema) *graphql.Result {
67 | result := graphql.Do(graphql.Params{
68 | Schema: schema,
69 | RequestString: query,
70 | })
71 | if len(result.Errors) > 0 {
72 | fmt.Printf("wrong result, unexpected errors: %v", result.Errors)
73 | }
74 | return result
75 | }
76 |
77 | // root mutation
78 | var rootMutation = graphql.NewObject(graphql.ObjectConfig{
79 | Name: "RootMutation",
80 | Fields: graphql.Fields{
81 | "createHotel": &graphql.Field{
82 | Type: hotelType, // the return type for this field
83 | Description: "Create new hotel",
84 | Args: graphql.FieldConfigArgument{
85 | "displayName": &graphql.ArgumentConfig{
86 | Type: graphql.NewNonNull(graphql.String),
87 | },
88 | "city": &graphql.ArgumentConfig{
89 | Type: graphql.NewNonNull(graphql.String),
90 | },
91 | "noRooms": &graphql.ArgumentConfig{
92 | Type: graphql.NewNonNull(graphql.Int),
93 | },
94 | "starRating": &graphql.ArgumentConfig{
95 | Type: graphql.NewNonNull(graphql.Int),
96 | },
97 | },
98 | Resolve: func(params graphql.ResolveParams) (interface{}, error) {
99 | // marshall and cast the argument value
100 | displayName, _ := params.Args["displayName"].(string)
101 | city, _ := params.Args["city"].(string)
102 | noRooms, _ := params.Args["noRooms"].(int)
103 | starRating, _ := params.Args["starRating"].(int)
104 |
105 | // create in 'DB'
106 | newHotel := Hotel{
107 | Id: randomId(),
108 | DisplayName: displayName,
109 | City: city,
110 | NoRooms: noRooms,
111 | StarRating: starRating,
112 | }
113 | hotels[newHotel.Id] = newHotel
114 |
115 | // return the new Hotel object
116 | return newHotel, nil
117 | },
118 | },
119 | },
120 | })
121 |
122 | //root query
123 | var rootQuery = graphql.NewObject(graphql.ObjectConfig{
124 | Name: "RootQuery",
125 | Fields: graphql.Fields{
126 |
127 | "hotel": &graphql.Field{
128 | Type: hotelType,
129 | Description: "Get a hotel with this id",
130 | Args: graphql.FieldConfigArgument{
131 | "id": &graphql.ArgumentConfig{
132 | Type: graphql.String,
133 | },
134 | },
135 | Resolve: func(params graphql.ResolveParams) (interface{}, error) {
136 | id, _ := params.Args["id"].(string)
137 | return hotels[id], nil
138 | },
139 | },
140 | },
141 | })
142 |
143 | // repository
144 | var hotels map[string]Hotel
145 |
146 | func init() {
147 | hotels = make(map[string]Hotel)
148 | }
149 |
150 | // Random ID Generator
151 | var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
152 |
153 | func randomId() string {
154 | b := make([]rune, 8)
155 | for i := range b {
156 | b[i] = letterRunes[rand.Intn(len(letterRunes))]
157 | }
158 | return string(b)
159 | }
160 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # Hands-On Software Architecture with Golang
5 |
6 |
7 |
8 | This is the code repository for [Hands-On Software Architecture with Golang](https://www.packtpub.com/application-development/hands-software-architecture-golang?utm_source=github&utm_medium=repository&utm_campaign=9781788622592), published by Packt.
9 |
10 | **Design and architect highly scalable and robust applications using Go**
11 |
12 | ## What is this book about?
13 | Building software requires careful planning and architectural considerations; Golang was developed with a fresh perspective on building next-generation applications on the cloud with distributed and concurrent computing concerns.
14 |
15 | This book covers the following exciting features:
16 | * Understand architectural paradigms and deep dive into Microservices
17 | * Design parallelism/concurrency patterns and learn object-oriented design patterns in Go
18 | * Explore API-driven systems architecture with introduction to REST and GraphQL standards
19 | * Build event-driven architectures and make your architectures anti-fragile
20 | * Engineer scalability and learn how to migrate to Go from other languages
21 | * Get to grips with deployment considerations with CICD pipeline, cloud deployments, and so on
22 | * Build an end-to-end e-commerce (travel) application backend in Go
23 |
24 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1788622596) today!
25 |
26 |
28 |
29 | ## Instructions and Navigations
30 | All of the code is organized into folders. For example, Chapter02.
31 |
32 | The code will look like the following:
33 | ```
34 | type Args struct {
35 | A, B int
36 | }
37 |
38 | type MuliplyService struct{}
39 |
40 | func (t *Arith) Do(args *Args, reply *int) error {
41 | *reply = args.A * args.B
42 | return nil
43 | }
44 | ```
45 |
46 | **Following is what you need for this book:**
47 | Hands-On Software Architecture with Golang is for software developers, architects, and CTOs looking to use Go in their software architecture to build enterprise-grade applications. Programming knowledge of Golang is assumed.
48 |
49 | With the following software and hardware list you can run all code files present in the book (Chapter 1-12).
50 | ### Software and Hardware List
51 | | Chapter | Software required | OS required |
52 | | -------- | ------------------------------------ | ----------------------------------- |
53 | | 1-12 | CURL, Go (preferably v 0.9), Git, Cassandra, Kafka, Redis, and NSQ | Windows, Mac OS X, and Linux (Any) |
54 |
55 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](http://www.packtpub.com/sites/default/files/downloads/9781788622592_ColorImages.pdf).
56 |
57 | ### Related products
58 | * Hands-On Serverless Applications with Go [[Packt]](https://www.packtpub.com/application-development/hands-serverless-applications-go?utm_source=github&utm_medium=repository&utm_campaign=9781789134612) [[Amazon]](https://www.amazon.com/dp/B07DT9DD4V)
59 |
60 | * Mastering Go [[Packt]](https://www.packtpub.com/networking-and-servers/mastering-go?utm_source=github&utm_medium=repository&utm_campaign=9781788626545) [[Amazon]](https://www.amazon.com/dp/1788626540)
61 |
62 | ## Get to Know the Author
63 | **Jyotiswarup Raiturkar**
64 | has architected products ranging from high-volume e-commerce sites to core infrastructure products. Notable products include the Walmart Labs Ecommerce Fulfillment Platform, Intuit Mint, SellerApp, Goibibo, Microsoft Virtual Server, and ShiftPixy, to name a few. Nowadays, he codes in Golang, Python, and Java.
65 |
66 | ### Suggestions and Feedback
67 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions.
68 |
69 |
70 | ### Download a free PDF
71 |
72 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
73 |