From ff9a3d39dd72563001513ba200efacf313b6c9bc Mon Sep 17 00:00:00 2001 From: Oren Rosen Date: Fri, 25 Nov 2022 16:27:26 +0200 Subject: [PATCH 01/10] delete orig --- coolfacts/entrypoint/build-and-run.md | 48 ------ coolfacts/entrypoint/main.go | 9 -- coolfacts/exercise1/go.mod | 3 - coolfacts/exercise1/main.go | 28 ---- coolfacts/exercise2/fact.go | 19 --- coolfacts/exercise2/go.mod | 3 - coolfacts/exercise2/main.go | 63 -------- coolfacts/exercise3/fact.go | 19 --- coolfacts/exercise3/go.mod | 3 - coolfacts/exercise3/main.go | 100 ------------ coolfacts/exercise4/fact.go | 19 --- coolfacts/exercise4/go.mod | 3 - coolfacts/exercise4/main.go | 139 ----------------- coolfacts/exercise5/fact.go | 19 --- coolfacts/exercise5/go.mod | 3 - coolfacts/exercise5/main.go | 139 ----------------- coolfacts/exercise5/mentalfloss.go | 57 ------- coolfacts/exercise6/fact/fact.go | 19 --- coolfacts/exercise6/fact/fact_test.go | 25 --- coolfacts/exercise6/go.mod | 3 - coolfacts/exercise6/http/handler.go | 130 ---------------- coolfacts/exercise6/main.go | 40 ----- .../exercise6/mentalfloss/mentalfloss.go | 57 ------- coolfacts/exercise7/fact/fact.go | 19 --- coolfacts/exercise7/go.mod | 3 - coolfacts/exercise7/go.sum | 0 coolfacts/exercise7/http/handler.go | 130 ---------------- coolfacts/exercise7/main.go | 73 --------- .../exercise7/mentalfloss/mentalfloss.go | 57 ------- coolfacts/exercise8/fact/fact.go | 6 - coolfacts/exercise8/fact/service.go | 39 ----- coolfacts/exercise8/go.mod | 3 - coolfacts/exercise8/go.sum | 2 - coolfacts/exercise8/http/handler.go | 142 ------------------ coolfacts/exercise8/inmem/factstore.go | 22 --- coolfacts/exercise8/main.go | 59 -------- coolfacts/exercise8/mentalfloss/item.go | 15 -- coolfacts/exercise8/mentalfloss/provider.go | 58 ------- coolfacts/go.mod | 25 +-- coolfacts/styles.css | 30 ---- gopresent/interfaces/example1/main.go | 8 +- .../example1/something/something.go | 4 +- gopresent/interfaces/example1/speaker.go | 3 +- gopresent/pointers/value1.go | 4 +- gopresent/pointers/value2.go | 6 +- 45 files changed, 13 insertions(+), 1643 deletions(-) delete mode 100644 coolfacts/entrypoint/build-and-run.md delete mode 100644 coolfacts/entrypoint/main.go delete mode 100644 coolfacts/exercise1/go.mod delete mode 100644 coolfacts/exercise1/main.go delete mode 100644 coolfacts/exercise2/fact.go delete mode 100644 coolfacts/exercise2/go.mod delete mode 100644 coolfacts/exercise2/main.go delete mode 100644 coolfacts/exercise3/fact.go delete mode 100644 coolfacts/exercise3/go.mod delete mode 100644 coolfacts/exercise3/main.go delete mode 100644 coolfacts/exercise4/fact.go delete mode 100644 coolfacts/exercise4/go.mod delete mode 100644 coolfacts/exercise4/main.go delete mode 100644 coolfacts/exercise5/fact.go delete mode 100644 coolfacts/exercise5/go.mod delete mode 100644 coolfacts/exercise5/main.go delete mode 100644 coolfacts/exercise5/mentalfloss.go delete mode 100644 coolfacts/exercise6/fact/fact.go delete mode 100644 coolfacts/exercise6/fact/fact_test.go delete mode 100644 coolfacts/exercise6/go.mod delete mode 100644 coolfacts/exercise6/http/handler.go delete mode 100644 coolfacts/exercise6/main.go delete mode 100644 coolfacts/exercise6/mentalfloss/mentalfloss.go delete mode 100644 coolfacts/exercise7/fact/fact.go delete mode 100644 coolfacts/exercise7/go.mod delete mode 100644 coolfacts/exercise7/go.sum delete mode 100644 coolfacts/exercise7/http/handler.go delete mode 100644 coolfacts/exercise7/main.go delete mode 100644 coolfacts/exercise7/mentalfloss/mentalfloss.go delete mode 100644 coolfacts/exercise8/fact/fact.go delete mode 100644 coolfacts/exercise8/fact/service.go delete mode 100644 coolfacts/exercise8/go.mod delete mode 100644 coolfacts/exercise8/go.sum delete mode 100644 coolfacts/exercise8/http/handler.go delete mode 100644 coolfacts/exercise8/inmem/factstore.go delete mode 100644 coolfacts/exercise8/main.go delete mode 100644 coolfacts/exercise8/mentalfloss/item.go delete mode 100644 coolfacts/exercise8/mentalfloss/provider.go delete mode 100644 coolfacts/styles.css diff --git a/coolfacts/entrypoint/build-and-run.md b/coolfacts/entrypoint/build-and-run.md deleted file mode 100644 index d21bee0..0000000 --- a/coolfacts/entrypoint/build-and-run.md +++ /dev/null @@ -1,48 +0,0 @@ -# Building and Running Go Applications - -The Go compiler creates an executable that is run directly on your system. - -## Go Build - -```bash -usage: go build [-o output] [-i] [build flags] [packages] -``` - -For example, if you want to build `entrypoint`: - -```bash -$ cd coolfacts/entrypoint -$ go build . -``` - -This will create a binary called `entrypoint` in the current directory. -(`entrypoint.exe` on windows) - -```bash -$ ls -l --rwxr-xr-x [...] entrypoint --rw-r--r-- [...] main.go -``` - -And now run the binary directly on your operating system - -```bash -$ ./entrypoint -Hello World! -``` - -For development, there's an easier way -for locally running a Go application - -```bash -$ go run . -Hello World! -``` - -## Advanced - -If you want to know more, the best place to start is the output of - -```bash -$ go help build -``` diff --git a/coolfacts/entrypoint/main.go b/coolfacts/entrypoint/main.go deleted file mode 100644 index b6dc2c8..0000000 --- a/coolfacts/entrypoint/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "fmt" -) - -func main() { - fmt.Println("Welcome to the go-workshop, hope you will have fun!!") -} diff --git a/coolfacts/exercise1/go.mod b/coolfacts/exercise1/go.mod deleted file mode 100644 index 5527b1a..0000000 --- a/coolfacts/exercise1/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise1 - -go 1.13 diff --git a/coolfacts/exercise1/main.go b/coolfacts/exercise1/main.go deleted file mode 100644 index abf4066..0000000 --- a/coolfacts/exercise1/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" - "log" - "net/http" -) - -func main() { - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts/exercise2/fact.go b/coolfacts/exercise2/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts/exercise2/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts/exercise2/go.mod b/coolfacts/exercise2/go.mod deleted file mode 100644 index 9170812..0000000 --- a/coolfacts/exercise2/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise2 - -go 1.13 diff --git a/coolfacts/exercise2/main.go b/coolfacts/exercise2/main.go deleted file mode 100644 index c51354c..0000000 --- a/coolfacts/exercise2/main.go +++ /dev/null @@ -1,63 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - "net/http" -) - -func main() { - factsRepo := repo{} - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", - Description: "Did you know sonic is a hedgehog?!", - }) - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", - Description: "You won't believe what happened to Arya!", - }) - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - // We are checking that we are answering only for GET method - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "application/json") - - // Here we are getting the JSON encoding of for the all facts from the repo - b, err := json.Marshal(factsRepo.getAll()) - if err != nil { - errMessage := fmt.Sprintf("error marshaling facts : %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - // Next we are writing it the the http.ResponseWriter - _, err = w.Write(b) - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts/exercise3/fact.go b/coolfacts/exercise3/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts/exercise3/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts/exercise3/go.mod b/coolfacts/exercise3/go.mod deleted file mode 100644 index 097c981..0000000 --- a/coolfacts/exercise3/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise3 - -go 1.13 diff --git a/coolfacts/exercise3/main.go b/coolfacts/exercise3/main.go deleted file mode 100644 index ba7abaf..0000000 --- a/coolfacts/exercise3/main.go +++ /dev/null @@ -1,100 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" -) - -func main() { - factsRepo := repo{} - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", - Description: "Did you know sonic is a hedgehog?!", - }) - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", - Description: "You won't believe what happened to Arya!", - }) - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - - w.Header().Add("Content-Type", "text/plain") - - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet { - w.Header().Add("Content-Type", "application/json") - - b, err := json.Marshal(factsRepo.getAll()) - if err != nil { - errMessage := fmt.Sprintf("error marshaling facts : %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - _, err = w.Write(b) - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - } - if r.Method == http.MethodPost { - // first read the request body into a byte stream - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - // We are expecting the payload to be from the form: - // { - // "image": "image name", - // "url": "image/url", - // "description": "image description" - // } - // We will use this struct representation of the expected request body, and parse into it the data - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - - // parse the JSON-encoded data and stores the result in req - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - return - } - - // create a new fact from this request struct, and repo it using the repo - f := fact{ - Image: req.Image, - Description: req.Description, - } - - factsRepo.add(f) - w.Write([]byte("SUCCESS")) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts/exercise4/fact.go b/coolfacts/exercise4/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts/exercise4/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts/exercise4/go.mod b/coolfacts/exercise4/go.mod deleted file mode 100644 index 6a8c003..0000000 --- a/coolfacts/exercise4/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise4 - -go 1.13 diff --git a/coolfacts/exercise4/main.go b/coolfacts/exercise4/main.go deleted file mode 100644 index fdc59da..0000000 --- a/coolfacts/exercise4/main.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "log" - "net/http" -) - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func main() { - factsRepo := repo{} - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", - Description: "Did you know sonic is a hedgehog?!", - }) - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", - Description: "You won't believe what happened to Arya!", - }) - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet { - w.Header().Add("Content-Type", "text/html") - - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - facts := factsRepo.getAll() - err = tmpl.Execute(w, facts) - if err != nil { - errMessage := fmt.Sprintf("failed parse template: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - return - } - if r.Method == http.MethodPost { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - - f := fact{ - Image: req.Image, - Description: req.Description, - } - - factsRepo.add(f) - w.Write([]byte("SUCCESS")) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts/exercise5/fact.go b/coolfacts/exercise5/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts/exercise5/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts/exercise5/go.mod b/coolfacts/exercise5/go.mod deleted file mode 100644 index ada5c65..0000000 --- a/coolfacts/exercise5/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise5 - -go 1.13 diff --git a/coolfacts/exercise5/main.go b/coolfacts/exercise5/main.go deleted file mode 100644 index 1ca150a..0000000 --- a/coolfacts/exercise5/main.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "log" - "net/http" -) - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func main() { - factsRepo := repo{} - mentalflossProvider := mentalfloss{} - facts, err := mentalflossProvider.Facts() - if err != nil { - log.Fatal("can't reach mentalfloss: ", err) - } - for _, f := range facts { - factsRepo.add(f) - } - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet { - w.Header().Add("Content-Type", "text/html") - - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - facts := factsRepo.getAll() - err = tmpl.Execute(w, facts) - if err != nil { - errMessage := fmt.Sprintf("failed parse template: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - return - } - if r.Method == http.MethodPost { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - - f := fact{ - Image: req.Image, - Description: req.Description, - } - - factsRepo.add(f) - w.Write([]byte("SUCCESS")) - } - }) - - log.Println("starting server") - err = http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts/exercise5/mentalfloss.go b/coolfacts/exercise5/mentalfloss.go deleted file mode 100644 index a62a622..0000000 --- a/coolfacts/exercise5/mentalfloss.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" -) - -type mentalfloss struct{} - -func (mf mentalfloss) Facts() ([]fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get("http://mentalfloss.com/api/facts") - if err != nil { - return nil, fmt.Errorf("error making request: %v", err) - } - log.Println("got facts from mentalfloss successfully") - - // A `defer` statement defers the execution of a function until the surrounding function returns. - // This is how we make sure that we close the response body before we exit the function. - defer resp.Body.Close() - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll: %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data: %v", err) - } - - return facts, nil -} - -func parseFromRawItems(b []byte) ([]fact, error) { - var items []struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` - } - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - var facts []fact - for _, it := range items { - newFact := fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts/exercise6/fact/fact.go b/coolfacts/exercise6/fact/fact.go deleted file mode 100644 index 2e15ecc..0000000 --- a/coolfacts/exercise6/fact/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package fact - -type Fact struct { - Image string - Description string -} - -type Repository struct { - facts []Fact -} - -func (r *Repository) Add(f Fact) { - // append is a Go built in function - r.facts = append(r.facts, f) -} - -func (r *Repository) GetAll() []Fact { - return r.facts -} diff --git a/coolfacts/exercise6/fact/fact_test.go b/coolfacts/exercise6/fact/fact_test.go deleted file mode 100644 index 7d76ecc..0000000 --- a/coolfacts/exercise6/fact/fact_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package fact - -import ( - "testing" -) - -func TestRepo_Add(t *testing.T) { - repo := Repository{} - fact := Fact{ - Image: "img", - Description: "desc", - } - repo.Add(fact) - - gotFacts := repo.GetAll() - var hasItem bool - for _, gotFact := range gotFacts { - if gotFact.Image == fact.Image && gotFact.Description == fact.Description { - hasItem = true - } - } - if !hasItem { - t.Errorf("can't find added fact") - } -} diff --git a/coolfacts/exercise6/go.mod b/coolfacts/exercise6/go.mod deleted file mode 100644 index 2cbafe9..0000000 --- a/coolfacts/exercise6/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise6 - -go 1.13 diff --git a/coolfacts/exercise6/http/handler.go b/coolfacts/exercise6/http/handler.go deleted file mode 100644 index dfd8598..0000000 --- a/coolfacts/exercise6/http/handler.go +++ /dev/null @@ -1,130 +0,0 @@ -package http - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" -) - -type FactsHandler struct { - FactRepo *fact.Repository -} - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func (h *FactsHandler) Ping(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } -} - -func (h *FactsHandler) Facts(w http.ResponseWriter, r *http.Request) { - if h.FactRepo == nil { - http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) - } - - switch r.Method { - case http.MethodGet: - h.showFacts(w) - return - case http.MethodPost: - h.postFacts(r, w) - return - default: - http.Error(w, "no http handler found", http.StatusNotFound) - } -} - -func (h *FactsHandler) postFacts(r *http.Request, w http.ResponseWriter) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - f := fact.Fact{ - Image: req.Image, - Description: req.Description, - } - h.FactRepo.Add(f) - w.Write([]byte("SUCCESS")) -} - -func (h *FactsHandler) showFacts(w http.ResponseWriter) { - w.Header().Add("Content-Type", "text/html") - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - tmpl.Execute(w, h.FactRepo.GetAll()) -} diff --git a/coolfacts/exercise6/main.go b/coolfacts/exercise6/main.go deleted file mode 100644 index dd8c193..0000000 --- a/coolfacts/exercise6/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" - facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise6/http" - "github.com/FTBpro/go-workshop/coolfacts/exercise6/mentalfloss" -) - -func main() { - factsRepo := fact.Repository{} - handlerer := facthttp.FactsHandler{ - FactRepo: &factsRepo, - } - - mentalflossProvider := mentalfloss.Mentalfloss{} - err := updateFacts(mentalflossProvider, &factsRepo) - - http.HandleFunc("/ping", handlerer.Ping) - http.HandleFunc("/facts", handlerer.Facts) - - log.Println("starting server") - err = http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} - -func updateFacts(mf mentalfloss.Mentalfloss, factsRepo *fact.Repository) error { - facts, err := mf.Facts() - if err != nil { - log.Fatal("can't reach mentalfloss: ", err) - } - for _, f := range facts { - factsRepo.Add(f) - } - return err -} diff --git a/coolfacts/exercise6/mentalfloss/mentalfloss.go b/coolfacts/exercise6/mentalfloss/mentalfloss.go deleted file mode 100644 index 04923d3..0000000 --- a/coolfacts/exercise6/mentalfloss/mentalfloss.go +++ /dev/null @@ -1,57 +0,0 @@ -package mentalfloss - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" -) - -type Mentalfloss struct { -} - -func (mf Mentalfloss) Facts() ([]fact.Fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get("http://mentalfloss.com/api/facts") - if err != nil { - return nil, fmt.Errorf("error making request: %v", err) - } - defer resp.Body.Close() - log.Println("got facts from mentalfloss successfully") - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll: %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data: %v", err) - } - - return facts, nil -} - -func parseFromRawItems(b []byte) ([]fact.Fact, error) { - var items []struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` - } - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - var facts []fact.Fact - for _, it := range items { - newFact := fact.Fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts/exercise7/fact/fact.go b/coolfacts/exercise7/fact/fact.go deleted file mode 100644 index 0b3485d..0000000 --- a/coolfacts/exercise7/fact/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package fact - -type Fact struct { - Image string - Description string -} - -type Repository struct { - facts []Fact -} - -func (r *Repository) Add(f Fact) { - // append is a Go built in function - r.facts = append([]Fact{f}, r.facts...) -} - -func (r *Repository) GetAll() []Fact { - return r.facts -} diff --git a/coolfacts/exercise7/go.mod b/coolfacts/exercise7/go.mod deleted file mode 100644 index 75ed072..0000000 --- a/coolfacts/exercise7/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise7 - -go 1.12 diff --git a/coolfacts/exercise7/go.sum b/coolfacts/exercise7/go.sum deleted file mode 100644 index e69de29..0000000 diff --git a/coolfacts/exercise7/http/handler.go b/coolfacts/exercise7/http/handler.go deleted file mode 100644 index af02f38..0000000 --- a/coolfacts/exercise7/http/handler.go +++ /dev/null @@ -1,130 +0,0 @@ -package http - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" -) - -type FactsHandler struct { - FactRepo *fact.Repository -} - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func (h *FactsHandler) Ping(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } -} - -func (h *FactsHandler) Facts(w http.ResponseWriter, r *http.Request) { - if h.FactRepo == nil { - http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) - } - - switch r.Method { - case http.MethodGet: - h.showFacts(w) - return - case http.MethodPost: - h.postFacts(r, w) - return - default: - http.Error(w, "no http handler found", http.StatusNotFound) - } -} - -func (h *FactsHandler) postFacts(r *http.Request, w http.ResponseWriter) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - f := fact.Fact{ - Image: req.Image, - Description: req.Description, - } - h.FactRepo.Add(f) - w.Write([]byte("SUCCESS")) -} - -func (h *FactsHandler) showFacts(w http.ResponseWriter) { - w.Header().Add("Content-Type", "text/html") - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - tmpl.Execute(w, h.FactRepo.GetAll()) -} diff --git a/coolfacts/exercise7/main.go b/coolfacts/exercise7/main.go deleted file mode 100644 index 16d6414..0000000 --- a/coolfacts/exercise7/main.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "context" - "log" - "net/http" - "time" - - "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" - facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise7/http" - "github.com/FTBpro/go-workshop/coolfacts/exercise7/mentalfloss" -) - -const ( - updateFactInterval = time.Minute * 1 -) - -func main() { - factsRepo := fact.Repository{} - handlerer := facthttp.FactsHandler{ - FactRepo: &factsRepo, - } - - mentalflossProvider := mentalfloss.Mentalfloss{} - updateFunc := updateFactsFunc(mentalflossProvider, &factsRepo) - if err := updateFunc(); err != nil { - log.Fatal(err) - } - - ctx, closer := context.WithCancel(context.Background()) - defer closer() - updateFactsWithTicker(ctx, updateFunc) - - http.HandleFunc("/ping", handlerer.Ping) - http.HandleFunc("/facts", handlerer.Facts) - - const port = ":9002" - log.Println("started server on http://localhost" + port) - log.Fatal(http.ListenAndServe(port, nil)) -} - -// private - -func updateFactsWithTicker(ctx context.Context, updateFunc func() error) { - tk := time.NewTicker(updateFactInterval) - go func(c context.Context) { - for { - select { - case <-tk.C: - log.Println("updating...") - if err := updateFunc(); err != nil { - log.Printf("error updating = %v", err) - } - log.Println("updated Successfully") - case <-c.Done(): - return - } - } - }(ctx) -} - -func updateFactsFunc(mf mentalfloss.Mentalfloss, factsRepo *fact.Repository) func() error { - return func() error { - facts, err := mf.Facts() - if err != nil { - log.Fatal("can't reach mentalfloss: ", err) - } - for _, f := range facts { - factsRepo.Add(f) - } - return err - } -} diff --git a/coolfacts/exercise7/mentalfloss/mentalfloss.go b/coolfacts/exercise7/mentalfloss/mentalfloss.go deleted file mode 100644 index c3e127a..0000000 --- a/coolfacts/exercise7/mentalfloss/mentalfloss.go +++ /dev/null @@ -1,57 +0,0 @@ -package mentalfloss - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" -) - -type Mentalfloss struct { -} - -func (mf Mentalfloss) Facts() ([]fact.Fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get("http://mentalfloss.com/api/facts") - if err != nil { - return nil, fmt.Errorf("error making request: %v", err) - } - defer resp.Body.Close() - log.Println("got facts from mentalfloss successfully") - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll: %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data: %v", err) - } - - return facts, nil -} - -func parseFromRawItems(b []byte) ([]fact.Fact, error) { - var items []struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` - } - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - var facts []fact.Fact - for _, it := range items { - newFact := fact.Fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts/exercise8/fact/fact.go b/coolfacts/exercise8/fact/fact.go deleted file mode 100644 index 65d33de..0000000 --- a/coolfacts/exercise8/fact/fact.go +++ /dev/null @@ -1,6 +0,0 @@ -package fact - -type Fact struct { - Image string - Description string -} diff --git a/coolfacts/exercise8/fact/service.go b/coolfacts/exercise8/fact/service.go deleted file mode 100644 index 37a82e2..0000000 --- a/coolfacts/exercise8/fact/service.go +++ /dev/null @@ -1,39 +0,0 @@ -package fact - -import ( - "fmt" -) - -type Provider interface { - Facts() ([]Fact, error) -} - -type Repository interface { - Add(f Fact) - GetAll() []Fact -} - -type service struct { - provider Provider - repo Repository -} - -func NewService(p Provider, s Repository) *service { - return &service{ - provider: p, - repo: s, - } -} - -func (s *service) UpdateFacts() error { - facts, err := s.provider.Facts() - if err != nil { - return fmt.Errorf("fact.service.UpdateFacts failed returiev facts %v", err) - } - - for _, fact := range facts { - s.repo.Add(fact) - } - - return nil -} diff --git a/coolfacts/exercise8/go.mod b/coolfacts/exercise8/go.mod deleted file mode 100644 index 970b0a1..0000000 --- a/coolfacts/exercise8/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise8 - -go 1.13 diff --git a/coolfacts/exercise8/go.sum b/coolfacts/exercise8/go.sum deleted file mode 100644 index c7519f2..0000000 --- a/coolfacts/exercise8/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/FTBpro/go-workshop v0.0.0-20190430114424-99097f9b0d53 h1:JEwdzoVkee+6JmnlW4co6UWKtXpEDLnwcjaIVXe3gXQ= -github.com/FTBpro/go-workshop/coolfacts v0.0.0-20190430114424-99097f9b0d53 h1:U75PlbRup9ERtFo9lbZkW06h3kgEOJXKrFT7ODHmfMM= diff --git a/coolfacts/exercise8/http/handler.go b/coolfacts/exercise8/http/handler.go deleted file mode 100644 index 0e57291..0000000 --- a/coolfacts/exercise8/http/handler.go +++ /dev/null @@ -1,142 +0,0 @@ -package http - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" -) - -type FactRepository interface { - Add(f fact.Fact) - GetAll() []fact.Fact -} - -type factsHandler struct { - factRepo FactRepository -} - -func NewFactsHandler(factRepo FactRepository) *factsHandler { - return &factsHandler{ - factRepo: factRepo, - } -} - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - - -` - -func (h *factsHandler) Ping(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } -} - -func (h *factsHandler) Facts(w http.ResponseWriter, r *http.Request) { - if h.factRepo == nil { - http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) - } - - switch r.Method { - case http.MethodGet: - h.showFacts(w) - return - case http.MethodPost: - h.postFacts(r, w) - return - default: - http.Error(w, "no http handler found", http.StatusNotFound) - } -} - -func (h *factsHandler) postFacts(r *http.Request, w http.ResponseWriter) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - f := fact.Fact{ - Image: req.Image, - Description: req.Description, - } - h.factRepo.Add(f) - w.Write([]byte("SUCCESS")) -} - -func (h *factsHandler) showFacts(w http.ResponseWriter) { - w.Header().Add("Content-Type", "text/html") - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - tmpl.Execute(w, h.factRepo.GetAll()) -} diff --git a/coolfacts/exercise8/inmem/factstore.go b/coolfacts/exercise8/inmem/factstore.go deleted file mode 100644 index e08433a..0000000 --- a/coolfacts/exercise8/inmem/factstore.go +++ /dev/null @@ -1,22 +0,0 @@ -package inmem - -import ( - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" -) - -type factRepository struct { - facts []fact.Fact -} - -func NewFactRepository() *factRepository { - return &factRepository{} -} - -func (r *factRepository) Add(f fact.Fact) { - // append is a Go built in function - r.facts = append(r.facts, f) -} - -func (r *factRepository) GetAll() []fact.Fact { - return r.facts -} diff --git a/coolfacts/exercise8/main.go b/coolfacts/exercise8/main.go deleted file mode 100644 index 5966893..0000000 --- a/coolfacts/exercise8/main.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "context" - "log" - "net/http" - "time" - - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" - facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise8/http" - "github.com/FTBpro/go-workshop/coolfacts/exercise8/inmem" - "github.com/FTBpro/go-workshop/coolfacts/exercise8/mentalfloss" -) - -const ( - updateFactInterval = 5 * time.Minute -) - -func main() { - factsRepository := inmem.NewFactRepository() - handler := facthttp.NewFactsHandler(factsRepository) - - mentalflossProvider := mentalfloss.NewProvider() - service := fact.NewService(mentalflossProvider, factsRepository) - if err := service.UpdateFacts(); err != nil { - log.Fatal("couldn't update facts, try later", err.Error()) - } - - ctx, closer := context.WithCancel(context.Background()) - defer closer() - updateFactsWithTicker(ctx, service.UpdateFacts) - - http.HandleFunc("/ping", handler.Ping) - http.HandleFunc("/facts", handler.Facts) - - const port = ":9002" - log.Println("started server on http://localhost" + port) - log.Fatal(http.ListenAndServe(port, nil)) -} - -// private - -func updateFactsWithTicker(ctx context.Context, updateFunc func() error) { - tk := time.NewTicker(updateFactInterval) - go func(c context.Context) { - for { - select { - case <-tk.C: - log.Println("updating...") - if err := updateFunc(); err != nil { - log.Printf("error updating = %v", err) - } - log.Println("updated Successfully") - case <-c.Done(): - return - } - } - }(ctx) -} diff --git a/coolfacts/exercise8/mentalfloss/item.go b/coolfacts/exercise8/mentalfloss/item.go deleted file mode 100644 index 3ce677c..0000000 --- a/coolfacts/exercise8/mentalfloss/item.go +++ /dev/null @@ -1,15 +0,0 @@ -package mentalfloss - -import "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" - -type item struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` -} - -func (it item) ToFact() fact.Fact { - return fact.Fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } -} diff --git a/coolfacts/exercise8/mentalfloss/provider.go b/coolfacts/exercise8/mentalfloss/provider.go deleted file mode 100644 index 08cd96c..0000000 --- a/coolfacts/exercise8/mentalfloss/provider.go +++ /dev/null @@ -1,58 +0,0 @@ -package mentalfloss - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" -) - -const getFactsAPI = "http://mentalfloss.com/api/facts" - -type provider struct{} - -func NewProvider() *provider { - return &provider{} -} - -func (p *provider) Facts() ([]fact.Fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get(getFactsAPI) - if err != nil { - return nil, fmt.Errorf("error get = %v", err) - } - defer resp.Body.Close() - - log.Println("got facts from mentalfloss successfully") - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll = %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data = %v", err) - } - - return facts, nil -} - -// private - -func parseFromRawItems(b []byte) ([]fact.Fact, error) { - items := make([]item, 0) - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - facts := make([]fact.Fact, 0) - for _, it := range items { - newFact := it.ToFact() - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts/go.mod b/coolfacts/go.mod index 13efe50..aded86a 100644 --- a/coolfacts/go.mod +++ b/coolfacts/go.mod @@ -1,30 +1,7 @@ module github.com/FTBpro/go-workshop/coolfacts require ( - github.com/FTBpro/go-workshop/coolfacts/exercise1 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise2 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise3 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise4 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise5 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise6 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise7 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise8 v0.0.0 -) - -replace github.com/FTBpro/go-workshop/coolfacts/exercise1 => ./exercise1 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise2 => ./exercise2 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise3 => ./exercise3 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise4 => ./exercise4 -replace github.com/FTBpro/go-workshop/coolfacts/exercise5 => ./exercise5 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise6 => ./exercise6 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise7 => ./exercise7 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise8 => ./exercise8 +) go 1.13 diff --git a/coolfacts/styles.css b/coolfacts/styles.css deleted file mode 100644 index a30fb11..0000000 --- a/coolfacts/styles.css +++ /dev/null @@ -1,30 +0,0 @@ -/* This file is not magically included anywhere*/ -body { - font-family: Helvetica, Arial, sans-serif; - color: #26323d; - max-width: 720px; - margin: auto; -} - -article { - border: 1px solid #0095c4; - border-radius: 4px; - max-width: 320px; - text-align: center; -} - -article h3 { - font-weight: normal; -} - -article a { - color: #26323d; -} - -article a:hover { - color: #f16957; -} - -article img { - border-radius: 4px; -} diff --git a/gopresent/interfaces/example1/main.go b/gopresent/interfaces/example1/main.go index c445f00..4cba19d 100644 --- a/gopresent/interfaces/example1/main.go +++ b/gopresent/interfaces/example1/main.go @@ -1,7 +1,7 @@ package main import "fmt" - + type Action interface { Do(int, int) int } @@ -10,14 +10,15 @@ func DoSomething(x, y int, action Action) { fmt.Printf("x = %d, y = %d, action.Do(x, y) = %d\n", x, y, action.Do(x, y)) } - // start OMIT type adder struct{} + func (a adder) Do(x, y int) int { return x + y } type multiplier struct{} + func (m multiplier) Do(x, y int) int { return x * y } @@ -26,4 +27,5 @@ func main() { DoSomething(3, 4, adder{}) DoSomething(3, 4, multiplier{}) } -// end OMIT \ No newline at end of file + +// end OMIT diff --git a/gopresent/interfaces/example1/something/something.go b/gopresent/interfaces/example1/something/something.go index e483ba6..974431a 100644 --- a/gopresent/interfaces/example1/something/something.go +++ b/gopresent/interfaces/example1/something/something.go @@ -4,11 +4,11 @@ import ( "fmt" ) -type Actioner interface{ +type Actioner interface { Do(int, int) int } func Do(x, y int, action Actioner) { result := action.Do(x, y) fmt.Printf("x = %d, y = %d, action.Do(x, y) = %d\n", x, y, result) -} \ No newline at end of file +} diff --git a/gopresent/interfaces/example1/speaker.go b/gopresent/interfaces/example1/speaker.go index 9283b0c..0557e8f 100644 --- a/gopresent/interfaces/example1/speaker.go +++ b/gopresent/interfaces/example1/speaker.go @@ -1,8 +1,9 @@ package main type Lala string + // type speaker struct{} // func (s speaker) Speak() { // fmt.Println("Hello, this is speaker") -// } \ No newline at end of file +// } diff --git a/gopresent/pointers/value1.go b/gopresent/pointers/value1.go index b4c341c..7696e6a 100644 --- a/gopresent/pointers/value1.go +++ b/gopresent/pointers/value1.go @@ -4,7 +4,7 @@ import ( "fmt" ) -func main(){ +func main() { a := 42 b := a // HL @@ -13,5 +13,3 @@ func main(){ fmt.Printf("value of a:\t%d\n", a) fmt.Printf("address of a:\t%p\n", &a) } - - diff --git a/gopresent/pointers/value2.go b/gopresent/pointers/value2.go index 23f2be5..dd81aab 100644 --- a/gopresent/pointers/value2.go +++ b/gopresent/pointers/value2.go @@ -4,9 +4,9 @@ import ( "fmt" ) -func main(){ +func main() { a := 42 - do := func (b int) { // HL + do := func(b int) { // HL fmt.Printf("value of b:\t%d\n", b) fmt.Printf("address of b:\t%p\n", &b) } @@ -15,5 +15,3 @@ func main(){ fmt.Printf("value of a:\t%d\n", a) fmt.Printf("address of a:\t%p\n", &a) } - - From 261889ca57c96027602afae9ee3fe19342a7478f Mon Sep 17 00:00:00 2001 From: Oren Rosen Date: Fri, 25 Nov 2022 16:28:48 +0200 Subject: [PATCH 02/10] add initial --- coolfacts/build-and-run.md | 48 ++++++++++++++++++++++++++++++++++++++ coolfacts/cmd/main.go | 5 ++++ coolfacts/go.mod | 2 +- 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 coolfacts/build-and-run.md create mode 100644 coolfacts/cmd/main.go diff --git a/coolfacts/build-and-run.md b/coolfacts/build-and-run.md new file mode 100644 index 0000000..d21bee0 --- /dev/null +++ b/coolfacts/build-and-run.md @@ -0,0 +1,48 @@ +# Building and Running Go Applications + +The Go compiler creates an executable that is run directly on your system. + +## Go Build + +```bash +usage: go build [-o output] [-i] [build flags] [packages] +``` + +For example, if you want to build `entrypoint`: + +```bash +$ cd coolfacts/entrypoint +$ go build . +``` + +This will create a binary called `entrypoint` in the current directory. +(`entrypoint.exe` on windows) + +```bash +$ ls -l +-rwxr-xr-x [...] entrypoint +-rw-r--r-- [...] main.go +``` + +And now run the binary directly on your operating system + +```bash +$ ./entrypoint +Hello World! +``` + +For development, there's an easier way +for locally running a Go application + +```bash +$ go run . +Hello World! +``` + +## Advanced + +If you want to know more, the best place to start is the output of + +```bash +$ go help build +``` diff --git a/coolfacts/cmd/main.go b/coolfacts/cmd/main.go new file mode 100644 index 0000000..16f28ba --- /dev/null +++ b/coolfacts/cmd/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + //TODO: print "Hello, World!" +} diff --git a/coolfacts/go.mod b/coolfacts/go.mod index aded86a..3fc4f15 100644 --- a/coolfacts/go.mod +++ b/coolfacts/go.mod @@ -4,4 +4,4 @@ require ( ) -go 1.13 +go 1.19 From 6a6c92a07834f058c92af18e39c9da03cd428847 Mon Sep 17 00:00:00 2001 From: Oren Rosen Date: Fri, 25 Nov 2022 16:29:28 +0200 Subject: [PATCH 03/10] add dep --- coolfacts-dep/exercise1/go.mod | 3 + coolfacts-dep/exercise1/main.go | 28 ++++ coolfacts-dep/exercise2/fact.go | 19 +++ coolfacts-dep/exercise2/go.mod | 3 + coolfacts-dep/exercise2/main.go | 63 ++++++++ coolfacts-dep/exercise3/fact.go | 19 +++ coolfacts-dep/exercise3/go.mod | 3 + coolfacts-dep/exercise3/main.go | 100 ++++++++++++ coolfacts-dep/exercise4/fact.go | 19 +++ coolfacts-dep/exercise4/go.mod | 3 + coolfacts-dep/exercise4/main.go | 139 +++++++++++++++++ coolfacts-dep/exercise5/fact.go | 19 +++ coolfacts-dep/exercise5/go.mod | 3 + coolfacts-dep/exercise5/main.go | 139 +++++++++++++++++ coolfacts-dep/exercise5/mentalfloss.go | 57 +++++++ coolfacts-dep/exercise6/fact/fact.go | 19 +++ coolfacts-dep/exercise6/fact/fact_test.go | 25 +++ coolfacts-dep/exercise6/go.mod | 3 + coolfacts-dep/exercise6/http/handler.go | 130 ++++++++++++++++ coolfacts-dep/exercise6/main.go | 40 +++++ .../exercise6/mentalfloss/mentalfloss.go | 57 +++++++ coolfacts-dep/exercise7/fact/fact.go | 19 +++ coolfacts-dep/exercise7/go.mod | 3 + coolfacts-dep/exercise7/go.sum | 0 coolfacts-dep/exercise7/http/handler.go | 130 ++++++++++++++++ coolfacts-dep/exercise7/main.go | 73 +++++++++ .../exercise7/mentalfloss/mentalfloss.go | 57 +++++++ coolfacts-dep/exercise8/fact/fact.go | 6 + coolfacts-dep/exercise8/fact/service.go | 39 +++++ coolfacts-dep/exercise8/go.mod | 3 + coolfacts-dep/exercise8/go.sum | 2 + coolfacts-dep/exercise8/http/handler.go | 142 ++++++++++++++++++ coolfacts-dep/exercise8/inmem/factstore.go | 22 +++ coolfacts-dep/exercise8/main.go | 59 ++++++++ coolfacts-dep/exercise8/mentalfloss/item.go | 15 ++ .../exercise8/mentalfloss/provider.go | 58 +++++++ coolfacts-dep/go.mod | 30 ++++ coolfacts-dep/styles.css | 30 ++++ 38 files changed, 1579 insertions(+) create mode 100644 coolfacts-dep/exercise1/go.mod create mode 100644 coolfacts-dep/exercise1/main.go create mode 100644 coolfacts-dep/exercise2/fact.go create mode 100644 coolfacts-dep/exercise2/go.mod create mode 100644 coolfacts-dep/exercise2/main.go create mode 100644 coolfacts-dep/exercise3/fact.go create mode 100644 coolfacts-dep/exercise3/go.mod create mode 100644 coolfacts-dep/exercise3/main.go create mode 100644 coolfacts-dep/exercise4/fact.go create mode 100644 coolfacts-dep/exercise4/go.mod create mode 100644 coolfacts-dep/exercise4/main.go create mode 100644 coolfacts-dep/exercise5/fact.go create mode 100644 coolfacts-dep/exercise5/go.mod create mode 100644 coolfacts-dep/exercise5/main.go create mode 100644 coolfacts-dep/exercise5/mentalfloss.go create mode 100644 coolfacts-dep/exercise6/fact/fact.go create mode 100644 coolfacts-dep/exercise6/fact/fact_test.go create mode 100644 coolfacts-dep/exercise6/go.mod create mode 100644 coolfacts-dep/exercise6/http/handler.go create mode 100644 coolfacts-dep/exercise6/main.go create mode 100644 coolfacts-dep/exercise6/mentalfloss/mentalfloss.go create mode 100644 coolfacts-dep/exercise7/fact/fact.go create mode 100644 coolfacts-dep/exercise7/go.mod create mode 100644 coolfacts-dep/exercise7/go.sum create mode 100644 coolfacts-dep/exercise7/http/handler.go create mode 100644 coolfacts-dep/exercise7/main.go create mode 100644 coolfacts-dep/exercise7/mentalfloss/mentalfloss.go create mode 100644 coolfacts-dep/exercise8/fact/fact.go create mode 100644 coolfacts-dep/exercise8/fact/service.go create mode 100644 coolfacts-dep/exercise8/go.mod create mode 100644 coolfacts-dep/exercise8/go.sum create mode 100644 coolfacts-dep/exercise8/http/handler.go create mode 100644 coolfacts-dep/exercise8/inmem/factstore.go create mode 100644 coolfacts-dep/exercise8/main.go create mode 100644 coolfacts-dep/exercise8/mentalfloss/item.go create mode 100644 coolfacts-dep/exercise8/mentalfloss/provider.go create mode 100644 coolfacts-dep/go.mod create mode 100644 coolfacts-dep/styles.css diff --git a/coolfacts-dep/exercise1/go.mod b/coolfacts-dep/exercise1/go.mod new file mode 100644 index 0000000..5527b1a --- /dev/null +++ b/coolfacts-dep/exercise1/go.mod @@ -0,0 +1,3 @@ +module github.com/FTBpro/go-workshop/coolfacts/exercise1 + +go 1.13 diff --git a/coolfacts-dep/exercise1/main.go b/coolfacts-dep/exercise1/main.go new file mode 100644 index 0000000..abf4066 --- /dev/null +++ b/coolfacts-dep/exercise1/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "log" + "net/http" +) + +func main() { + http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "no http handler found", http.StatusNotFound) + return + } + w.Header().Add("Content-Type", "text/plain") + _, err := fmt.Fprint(w, "PONG") + if err != nil { + errMessage := fmt.Sprintf("error writing response: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + } + }) + + log.Println("starting server") + err := http.ListenAndServe(":9002", nil) + if err != nil { + log.Fatal(err) + } +} diff --git a/coolfacts-dep/exercise2/fact.go b/coolfacts-dep/exercise2/fact.go new file mode 100644 index 0000000..5023d28 --- /dev/null +++ b/coolfacts-dep/exercise2/fact.go @@ -0,0 +1,19 @@ +package main + +type fact struct { + Image string + Description string +} + +type repo struct { + facts []fact +} + +func (s *repo) add(f fact) { + // append is a Go built in function + s.facts = append(s.facts, f) +} + +func (s *repo) getAll() []fact { + return s.facts +} diff --git a/coolfacts-dep/exercise2/go.mod b/coolfacts-dep/exercise2/go.mod new file mode 100644 index 0000000..9170812 --- /dev/null +++ b/coolfacts-dep/exercise2/go.mod @@ -0,0 +1,3 @@ +module github.com/FTBpro/go-workshop/coolfacts/exercise2 + +go 1.13 diff --git a/coolfacts-dep/exercise2/main.go b/coolfacts-dep/exercise2/main.go new file mode 100644 index 0000000..c51354c --- /dev/null +++ b/coolfacts-dep/exercise2/main.go @@ -0,0 +1,63 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "net/http" +) + +func main() { + factsRepo := repo{} + factsRepo.add(fact{ + Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", + Description: "Did you know sonic is a hedgehog?!", + }) + factsRepo.add(fact{ + Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", + Description: "You won't believe what happened to Arya!", + }) + + http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "no http handler found", http.StatusNotFound) + return + } + w.Header().Add("Content-Type", "text/plain") + _, err := fmt.Fprint(w, "PONG") + if err != nil { + errMessage := fmt.Sprintf("error writing response: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + } + }) + + http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { + // We are checking that we are answering only for GET method + if r.Method != http.MethodGet { + http.Error(w, "no http handler found", http.StatusNotFound) + return + } + w.Header().Add("Content-Type", "application/json") + + // Here we are getting the JSON encoding of for the all facts from the repo + b, err := json.Marshal(factsRepo.getAll()) + if err != nil { + errMessage := fmt.Sprintf("error marshaling facts : %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + + // Next we are writing it the the http.ResponseWriter + _, err = w.Write(b) + if err != nil { + errMessage := fmt.Sprintf("error writing response: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + } + }) + + log.Println("starting server") + err := http.ListenAndServe(":9002", nil) + if err != nil { + log.Fatal(err) + } +} diff --git a/coolfacts-dep/exercise3/fact.go b/coolfacts-dep/exercise3/fact.go new file mode 100644 index 0000000..5023d28 --- /dev/null +++ b/coolfacts-dep/exercise3/fact.go @@ -0,0 +1,19 @@ +package main + +type fact struct { + Image string + Description string +} + +type repo struct { + facts []fact +} + +func (s *repo) add(f fact) { + // append is a Go built in function + s.facts = append(s.facts, f) +} + +func (s *repo) getAll() []fact { + return s.facts +} diff --git a/coolfacts-dep/exercise3/go.mod b/coolfacts-dep/exercise3/go.mod new file mode 100644 index 0000000..097c981 --- /dev/null +++ b/coolfacts-dep/exercise3/go.mod @@ -0,0 +1,3 @@ +module github.com/FTBpro/go-workshop/coolfacts/exercise3 + +go 1.13 diff --git a/coolfacts-dep/exercise3/main.go b/coolfacts-dep/exercise3/main.go new file mode 100644 index 0000000..ba7abaf --- /dev/null +++ b/coolfacts-dep/exercise3/main.go @@ -0,0 +1,100 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" +) + +func main() { + factsRepo := repo{} + factsRepo.add(fact{ + Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", + Description: "Did you know sonic is a hedgehog?!", + }) + factsRepo.add(fact{ + Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", + Description: "You won't believe what happened to Arya!", + }) + + http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "no http handler found", http.StatusNotFound) + return + } + + w.Header().Add("Content-Type", "text/plain") + + _, err := fmt.Fprint(w, "PONG") + if err != nil { + errMessage := fmt.Sprintf("error writing response: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + } + }) + + http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + w.Header().Add("Content-Type", "application/json") + + b, err := json.Marshal(factsRepo.getAll()) + if err != nil { + errMessage := fmt.Sprintf("error marshaling facts : %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + + _, err = w.Write(b) + if err != nil { + errMessage := fmt.Sprintf("error writing response: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + } + if r.Method == http.MethodPost { + // first read the request body into a byte stream + b, err := ioutil.ReadAll(r.Body) + if err != nil { + errMessage := fmt.Sprintf("error read from body: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + + // We are expecting the payload to be from the form: + // { + // "image": "image name", + // "url": "image/url", + // "description": "image description" + // } + // We will use this struct representation of the expected request body, and parse into it the data + var req struct { + Image string `json:"image"` + Description string `json:"description"` + } + + // parse the JSON-encoded data and stores the result in req + err = json.Unmarshal(b, &req) + if err != nil { + errMessage := fmt.Sprintf("error parsing fact: %v", err) + http.Error(w, errMessage, http.StatusBadRequest) + return + } + + // create a new fact from this request struct, and repo it using the repo + f := fact{ + Image: req.Image, + Description: req.Description, + } + + factsRepo.add(f) + w.Write([]byte("SUCCESS")) + } + }) + + log.Println("starting server") + err := http.ListenAndServe(":9002", nil) + if err != nil { + log.Fatal(err) + } +} diff --git a/coolfacts-dep/exercise4/fact.go b/coolfacts-dep/exercise4/fact.go new file mode 100644 index 0000000..5023d28 --- /dev/null +++ b/coolfacts-dep/exercise4/fact.go @@ -0,0 +1,19 @@ +package main + +type fact struct { + Image string + Description string +} + +type repo struct { + facts []fact +} + +func (s *repo) add(f fact) { + // append is a Go built in function + s.facts = append(s.facts, f) +} + +func (s *repo) getAll() []fact { + return s.facts +} diff --git a/coolfacts-dep/exercise4/go.mod b/coolfacts-dep/exercise4/go.mod new file mode 100644 index 0000000..6a8c003 --- /dev/null +++ b/coolfacts-dep/exercise4/go.mod @@ -0,0 +1,3 @@ +module github.com/FTBpro/go-workshop/coolfacts/exercise4 + +go 1.13 diff --git a/coolfacts-dep/exercise4/main.go b/coolfacts-dep/exercise4/main.go new file mode 100644 index 0000000..fdc59da --- /dev/null +++ b/coolfacts-dep/exercise4/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "encoding/json" + "fmt" + "html/template" + "io/ioutil" + "log" + "net/http" +) + +var newsTemplate = ` + + + Coolfacts + + + + +

Hear These Amazing Facts!

+ {{ range . }} +
+

{{.Description}}

+ +
+ {{ end }} + + +` + +func main() { + factsRepo := repo{} + factsRepo.add(fact{ + Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", + Description: "Did you know sonic is a hedgehog?!", + }) + factsRepo.add(fact{ + Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", + Description: "You won't believe what happened to Arya!", + }) + + http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "no http handler found", http.StatusNotFound) + return + } + w.Header().Add("Content-Type", "text/plain") + _, err := fmt.Fprint(w, "PONG") + if err != nil { + errMessage := fmt.Sprintf("error writing response: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + } + }) + + http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + w.Header().Add("Content-Type", "text/html") + + tmpl, err := template.New("facts").Parse(newsTemplate) + if err != nil { + errMessage := fmt.Sprintf("error ghttp template writing: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + + facts := factsRepo.getAll() + err = tmpl.Execute(w, facts) + if err != nil { + errMessage := fmt.Sprintf("failed parse template: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + + return + } + if r.Method == http.MethodPost { + b, err := ioutil.ReadAll(r.Body) + if err != nil { + errMessage := fmt.Sprintf("error read from body: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + var req struct { + Image string `json:"image"` + Description string `json:"description"` + } + err = json.Unmarshal(b, &req) + if err != nil { + errMessage := fmt.Sprintf("error parsing fact: %v", err) + http.Error(w, errMessage, http.StatusBadRequest) + } + + f := fact{ + Image: req.Image, + Description: req.Description, + } + + factsRepo.add(f) + w.Write([]byte("SUCCESS")) + } + }) + + log.Println("starting server") + err := http.ListenAndServe(":9002", nil) + if err != nil { + log.Fatal(err) + } +} diff --git a/coolfacts-dep/exercise5/fact.go b/coolfacts-dep/exercise5/fact.go new file mode 100644 index 0000000..5023d28 --- /dev/null +++ b/coolfacts-dep/exercise5/fact.go @@ -0,0 +1,19 @@ +package main + +type fact struct { + Image string + Description string +} + +type repo struct { + facts []fact +} + +func (s *repo) add(f fact) { + // append is a Go built in function + s.facts = append(s.facts, f) +} + +func (s *repo) getAll() []fact { + return s.facts +} diff --git a/coolfacts-dep/exercise5/go.mod b/coolfacts-dep/exercise5/go.mod new file mode 100644 index 0000000..ada5c65 --- /dev/null +++ b/coolfacts-dep/exercise5/go.mod @@ -0,0 +1,3 @@ +module github.com/FTBpro/go-workshop/coolfacts/exercise5 + +go 1.13 diff --git a/coolfacts-dep/exercise5/main.go b/coolfacts-dep/exercise5/main.go new file mode 100644 index 0000000..1ca150a --- /dev/null +++ b/coolfacts-dep/exercise5/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "encoding/json" + "fmt" + "html/template" + "io/ioutil" + "log" + "net/http" +) + +var newsTemplate = ` + + + Coolfacts + + + + +

Hear These Amazing Facts!

+ {{ range . }} +
+

{{.Description}}

+ +
+ {{ end }} + + +` + +func main() { + factsRepo := repo{} + mentalflossProvider := mentalfloss{} + facts, err := mentalflossProvider.Facts() + if err != nil { + log.Fatal("can't reach mentalfloss: ", err) + } + for _, f := range facts { + factsRepo.add(f) + } + + http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "no http handler found", http.StatusNotFound) + return + } + w.Header().Add("Content-Type", "text/plain") + _, err := fmt.Fprint(w, "PONG") + if err != nil { + errMessage := fmt.Sprintf("error writing response: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + } + }) + + http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + w.Header().Add("Content-Type", "text/html") + + tmpl, err := template.New("facts").Parse(newsTemplate) + if err != nil { + errMessage := fmt.Sprintf("error ghttp template writing: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + + facts := factsRepo.getAll() + err = tmpl.Execute(w, facts) + if err != nil { + errMessage := fmt.Sprintf("failed parse template: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + + return + } + if r.Method == http.MethodPost { + b, err := ioutil.ReadAll(r.Body) + if err != nil { + errMessage := fmt.Sprintf("error read from body: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + var req struct { + Image string `json:"image"` + Description string `json:"description"` + } + err = json.Unmarshal(b, &req) + if err != nil { + errMessage := fmt.Sprintf("error parsing fact: %v", err) + http.Error(w, errMessage, http.StatusBadRequest) + } + + f := fact{ + Image: req.Image, + Description: req.Description, + } + + factsRepo.add(f) + w.Write([]byte("SUCCESS")) + } + }) + + log.Println("starting server") + err = http.ListenAndServe(":9002", nil) + if err != nil { + log.Fatal(err) + } +} diff --git a/coolfacts-dep/exercise5/mentalfloss.go b/coolfacts-dep/exercise5/mentalfloss.go new file mode 100644 index 0000000..a62a622 --- /dev/null +++ b/coolfacts-dep/exercise5/mentalfloss.go @@ -0,0 +1,57 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" +) + +type mentalfloss struct{} + +func (mf mentalfloss) Facts() ([]fact, error) { + log.Println("getting facts from mentalfloss") + resp, err := http.Get("http://mentalfloss.com/api/facts") + if err != nil { + return nil, fmt.Errorf("error making request: %v", err) + } + log.Println("got facts from mentalfloss successfully") + + // A `defer` statement defers the execution of a function until the surrounding function returns. + // This is how we make sure that we close the response body before we exit the function. + defer resp.Body.Close() + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error readAll: %v", err) + } + + facts, err := parseFromRawItems(b) + if err != nil { + return nil, fmt.Errorf("error parsing data: %v", err) + } + + return facts, nil +} + +func parseFromRawItems(b []byte) ([]fact, error) { + var items []struct { + FactText string `json:"fact"` + PrimaryImage string `json:"primaryImage"` + } + if err := json.Unmarshal(b, &items); err != nil { + return nil, err + } + + var facts []fact + for _, it := range items { + newFact := fact{ + Image: it.PrimaryImage, + Description: it.FactText, + } + facts = append(facts, newFact) + } + + return facts, nil +} diff --git a/coolfacts-dep/exercise6/fact/fact.go b/coolfacts-dep/exercise6/fact/fact.go new file mode 100644 index 0000000..2e15ecc --- /dev/null +++ b/coolfacts-dep/exercise6/fact/fact.go @@ -0,0 +1,19 @@ +package fact + +type Fact struct { + Image string + Description string +} + +type Repository struct { + facts []Fact +} + +func (r *Repository) Add(f Fact) { + // append is a Go built in function + r.facts = append(r.facts, f) +} + +func (r *Repository) GetAll() []Fact { + return r.facts +} diff --git a/coolfacts-dep/exercise6/fact/fact_test.go b/coolfacts-dep/exercise6/fact/fact_test.go new file mode 100644 index 0000000..7d76ecc --- /dev/null +++ b/coolfacts-dep/exercise6/fact/fact_test.go @@ -0,0 +1,25 @@ +package fact + +import ( + "testing" +) + +func TestRepo_Add(t *testing.T) { + repo := Repository{} + fact := Fact{ + Image: "img", + Description: "desc", + } + repo.Add(fact) + + gotFacts := repo.GetAll() + var hasItem bool + for _, gotFact := range gotFacts { + if gotFact.Image == fact.Image && gotFact.Description == fact.Description { + hasItem = true + } + } + if !hasItem { + t.Errorf("can't find added fact") + } +} diff --git a/coolfacts-dep/exercise6/go.mod b/coolfacts-dep/exercise6/go.mod new file mode 100644 index 0000000..2cbafe9 --- /dev/null +++ b/coolfacts-dep/exercise6/go.mod @@ -0,0 +1,3 @@ +module github.com/FTBpro/go-workshop/coolfacts/exercise6 + +go 1.13 diff --git a/coolfacts-dep/exercise6/http/handler.go b/coolfacts-dep/exercise6/http/handler.go new file mode 100644 index 0000000..dfd8598 --- /dev/null +++ b/coolfacts-dep/exercise6/http/handler.go @@ -0,0 +1,130 @@ +package http + +import ( + "encoding/json" + "fmt" + "html/template" + "io/ioutil" + "net/http" + + "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" +) + +type FactsHandler struct { + FactRepo *fact.Repository +} + +var newsTemplate = ` + + + Coolfacts + + + + +

Hear These Amazing Facts!

+ {{ range . }} +
+

{{.Description}}

+ +
+ {{ end }} + + +` + +func (h *FactsHandler) Ping(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "no http handler found", http.StatusNotFound) + return + } + w.Header().Add("Content-Type", "text/plain") + _, err := fmt.Fprint(w, "PONG") + if err != nil { + errMessage := fmt.Sprintf("error writing response: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + } +} + +func (h *FactsHandler) Facts(w http.ResponseWriter, r *http.Request) { + if h.FactRepo == nil { + http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) + } + + switch r.Method { + case http.MethodGet: + h.showFacts(w) + return + case http.MethodPost: + h.postFacts(r, w) + return + default: + http.Error(w, "no http handler found", http.StatusNotFound) + } +} + +func (h *FactsHandler) postFacts(r *http.Request, w http.ResponseWriter) { + b, err := ioutil.ReadAll(r.Body) + if err != nil { + errMessage := fmt.Sprintf("error read from body: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + var req struct { + Image string `json:"image"` + Description string `json:"description"` + } + err = json.Unmarshal(b, &req) + if err != nil { + errMessage := fmt.Sprintf("error parsing fact: %v", err) + http.Error(w, errMessage, http.StatusBadRequest) + } + f := fact.Fact{ + Image: req.Image, + Description: req.Description, + } + h.FactRepo.Add(f) + w.Write([]byte("SUCCESS")) +} + +func (h *FactsHandler) showFacts(w http.ResponseWriter) { + w.Header().Add("Content-Type", "text/html") + tmpl, err := template.New("facts").Parse(newsTemplate) + if err != nil { + errMessage := fmt.Sprintf("error ghttp template writing: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + tmpl.Execute(w, h.FactRepo.GetAll()) +} diff --git a/coolfacts-dep/exercise6/main.go b/coolfacts-dep/exercise6/main.go new file mode 100644 index 0000000..dd8c193 --- /dev/null +++ b/coolfacts-dep/exercise6/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "log" + "net/http" + + "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" + facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise6/http" + "github.com/FTBpro/go-workshop/coolfacts/exercise6/mentalfloss" +) + +func main() { + factsRepo := fact.Repository{} + handlerer := facthttp.FactsHandler{ + FactRepo: &factsRepo, + } + + mentalflossProvider := mentalfloss.Mentalfloss{} + err := updateFacts(mentalflossProvider, &factsRepo) + + http.HandleFunc("/ping", handlerer.Ping) + http.HandleFunc("/facts", handlerer.Facts) + + log.Println("starting server") + err = http.ListenAndServe(":9002", nil) + if err != nil { + log.Fatal(err) + } +} + +func updateFacts(mf mentalfloss.Mentalfloss, factsRepo *fact.Repository) error { + facts, err := mf.Facts() + if err != nil { + log.Fatal("can't reach mentalfloss: ", err) + } + for _, f := range facts { + factsRepo.Add(f) + } + return err +} diff --git a/coolfacts-dep/exercise6/mentalfloss/mentalfloss.go b/coolfacts-dep/exercise6/mentalfloss/mentalfloss.go new file mode 100644 index 0000000..04923d3 --- /dev/null +++ b/coolfacts-dep/exercise6/mentalfloss/mentalfloss.go @@ -0,0 +1,57 @@ +package mentalfloss + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + + "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" +) + +type Mentalfloss struct { +} + +func (mf Mentalfloss) Facts() ([]fact.Fact, error) { + log.Println("getting facts from mentalfloss") + resp, err := http.Get("http://mentalfloss.com/api/facts") + if err != nil { + return nil, fmt.Errorf("error making request: %v", err) + } + defer resp.Body.Close() + log.Println("got facts from mentalfloss successfully") + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error readAll: %v", err) + } + + facts, err := parseFromRawItems(b) + if err != nil { + return nil, fmt.Errorf("error parsing data: %v", err) + } + + return facts, nil +} + +func parseFromRawItems(b []byte) ([]fact.Fact, error) { + var items []struct { + FactText string `json:"fact"` + PrimaryImage string `json:"primaryImage"` + } + if err := json.Unmarshal(b, &items); err != nil { + return nil, err + } + + var facts []fact.Fact + for _, it := range items { + newFact := fact.Fact{ + Image: it.PrimaryImage, + Description: it.FactText, + } + facts = append(facts, newFact) + } + + return facts, nil +} diff --git a/coolfacts-dep/exercise7/fact/fact.go b/coolfacts-dep/exercise7/fact/fact.go new file mode 100644 index 0000000..0b3485d --- /dev/null +++ b/coolfacts-dep/exercise7/fact/fact.go @@ -0,0 +1,19 @@ +package fact + +type Fact struct { + Image string + Description string +} + +type Repository struct { + facts []Fact +} + +func (r *Repository) Add(f Fact) { + // append is a Go built in function + r.facts = append([]Fact{f}, r.facts...) +} + +func (r *Repository) GetAll() []Fact { + return r.facts +} diff --git a/coolfacts-dep/exercise7/go.mod b/coolfacts-dep/exercise7/go.mod new file mode 100644 index 0000000..75ed072 --- /dev/null +++ b/coolfacts-dep/exercise7/go.mod @@ -0,0 +1,3 @@ +module github.com/FTBpro/go-workshop/coolfacts/exercise7 + +go 1.12 diff --git a/coolfacts-dep/exercise7/go.sum b/coolfacts-dep/exercise7/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/coolfacts-dep/exercise7/http/handler.go b/coolfacts-dep/exercise7/http/handler.go new file mode 100644 index 0000000..af02f38 --- /dev/null +++ b/coolfacts-dep/exercise7/http/handler.go @@ -0,0 +1,130 @@ +package http + +import ( + "encoding/json" + "fmt" + "html/template" + "io/ioutil" + "net/http" + + "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" +) + +type FactsHandler struct { + FactRepo *fact.Repository +} + +var newsTemplate = ` + + + Coolfacts + + + + +

Hear These Amazing Facts!

+ {{ range . }} +
+

{{.Description}}

+ +
+ {{ end }} + + +` + +func (h *FactsHandler) Ping(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "no http handler found", http.StatusNotFound) + return + } + w.Header().Add("Content-Type", "text/plain") + _, err := fmt.Fprint(w, "PONG") + if err != nil { + errMessage := fmt.Sprintf("error writing response: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + } +} + +func (h *FactsHandler) Facts(w http.ResponseWriter, r *http.Request) { + if h.FactRepo == nil { + http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) + } + + switch r.Method { + case http.MethodGet: + h.showFacts(w) + return + case http.MethodPost: + h.postFacts(r, w) + return + default: + http.Error(w, "no http handler found", http.StatusNotFound) + } +} + +func (h *FactsHandler) postFacts(r *http.Request, w http.ResponseWriter) { + b, err := ioutil.ReadAll(r.Body) + if err != nil { + errMessage := fmt.Sprintf("error read from body: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + var req struct { + Image string `json:"image"` + Description string `json:"description"` + } + err = json.Unmarshal(b, &req) + if err != nil { + errMessage := fmt.Sprintf("error parsing fact: %v", err) + http.Error(w, errMessage, http.StatusBadRequest) + } + f := fact.Fact{ + Image: req.Image, + Description: req.Description, + } + h.FactRepo.Add(f) + w.Write([]byte("SUCCESS")) +} + +func (h *FactsHandler) showFacts(w http.ResponseWriter) { + w.Header().Add("Content-Type", "text/html") + tmpl, err := template.New("facts").Parse(newsTemplate) + if err != nil { + errMessage := fmt.Sprintf("error ghttp template writing: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + tmpl.Execute(w, h.FactRepo.GetAll()) +} diff --git a/coolfacts-dep/exercise7/main.go b/coolfacts-dep/exercise7/main.go new file mode 100644 index 0000000..16d6414 --- /dev/null +++ b/coolfacts-dep/exercise7/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "context" + "log" + "net/http" + "time" + + "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" + facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise7/http" + "github.com/FTBpro/go-workshop/coolfacts/exercise7/mentalfloss" +) + +const ( + updateFactInterval = time.Minute * 1 +) + +func main() { + factsRepo := fact.Repository{} + handlerer := facthttp.FactsHandler{ + FactRepo: &factsRepo, + } + + mentalflossProvider := mentalfloss.Mentalfloss{} + updateFunc := updateFactsFunc(mentalflossProvider, &factsRepo) + if err := updateFunc(); err != nil { + log.Fatal(err) + } + + ctx, closer := context.WithCancel(context.Background()) + defer closer() + updateFactsWithTicker(ctx, updateFunc) + + http.HandleFunc("/ping", handlerer.Ping) + http.HandleFunc("/facts", handlerer.Facts) + + const port = ":9002" + log.Println("started server on http://localhost" + port) + log.Fatal(http.ListenAndServe(port, nil)) +} + +// private + +func updateFactsWithTicker(ctx context.Context, updateFunc func() error) { + tk := time.NewTicker(updateFactInterval) + go func(c context.Context) { + for { + select { + case <-tk.C: + log.Println("updating...") + if err := updateFunc(); err != nil { + log.Printf("error updating = %v", err) + } + log.Println("updated Successfully") + case <-c.Done(): + return + } + } + }(ctx) +} + +func updateFactsFunc(mf mentalfloss.Mentalfloss, factsRepo *fact.Repository) func() error { + return func() error { + facts, err := mf.Facts() + if err != nil { + log.Fatal("can't reach mentalfloss: ", err) + } + for _, f := range facts { + factsRepo.Add(f) + } + return err + } +} diff --git a/coolfacts-dep/exercise7/mentalfloss/mentalfloss.go b/coolfacts-dep/exercise7/mentalfloss/mentalfloss.go new file mode 100644 index 0000000..c3e127a --- /dev/null +++ b/coolfacts-dep/exercise7/mentalfloss/mentalfloss.go @@ -0,0 +1,57 @@ +package mentalfloss + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + + "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" +) + +type Mentalfloss struct { +} + +func (mf Mentalfloss) Facts() ([]fact.Fact, error) { + log.Println("getting facts from mentalfloss") + resp, err := http.Get("http://mentalfloss.com/api/facts") + if err != nil { + return nil, fmt.Errorf("error making request: %v", err) + } + defer resp.Body.Close() + log.Println("got facts from mentalfloss successfully") + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error readAll: %v", err) + } + + facts, err := parseFromRawItems(b) + if err != nil { + return nil, fmt.Errorf("error parsing data: %v", err) + } + + return facts, nil +} + +func parseFromRawItems(b []byte) ([]fact.Fact, error) { + var items []struct { + FactText string `json:"fact"` + PrimaryImage string `json:"primaryImage"` + } + if err := json.Unmarshal(b, &items); err != nil { + return nil, err + } + + var facts []fact.Fact + for _, it := range items { + newFact := fact.Fact{ + Image: it.PrimaryImage, + Description: it.FactText, + } + facts = append(facts, newFact) + } + + return facts, nil +} diff --git a/coolfacts-dep/exercise8/fact/fact.go b/coolfacts-dep/exercise8/fact/fact.go new file mode 100644 index 0000000..65d33de --- /dev/null +++ b/coolfacts-dep/exercise8/fact/fact.go @@ -0,0 +1,6 @@ +package fact + +type Fact struct { + Image string + Description string +} diff --git a/coolfacts-dep/exercise8/fact/service.go b/coolfacts-dep/exercise8/fact/service.go new file mode 100644 index 0000000..37a82e2 --- /dev/null +++ b/coolfacts-dep/exercise8/fact/service.go @@ -0,0 +1,39 @@ +package fact + +import ( + "fmt" +) + +type Provider interface { + Facts() ([]Fact, error) +} + +type Repository interface { + Add(f Fact) + GetAll() []Fact +} + +type service struct { + provider Provider + repo Repository +} + +func NewService(p Provider, s Repository) *service { + return &service{ + provider: p, + repo: s, + } +} + +func (s *service) UpdateFacts() error { + facts, err := s.provider.Facts() + if err != nil { + return fmt.Errorf("fact.service.UpdateFacts failed returiev facts %v", err) + } + + for _, fact := range facts { + s.repo.Add(fact) + } + + return nil +} diff --git a/coolfacts-dep/exercise8/go.mod b/coolfacts-dep/exercise8/go.mod new file mode 100644 index 0000000..970b0a1 --- /dev/null +++ b/coolfacts-dep/exercise8/go.mod @@ -0,0 +1,3 @@ +module github.com/FTBpro/go-workshop/coolfacts/exercise8 + +go 1.13 diff --git a/coolfacts-dep/exercise8/go.sum b/coolfacts-dep/exercise8/go.sum new file mode 100644 index 0000000..c7519f2 --- /dev/null +++ b/coolfacts-dep/exercise8/go.sum @@ -0,0 +1,2 @@ +github.com/FTBpro/go-workshop v0.0.0-20190430114424-99097f9b0d53 h1:JEwdzoVkee+6JmnlW4co6UWKtXpEDLnwcjaIVXe3gXQ= +github.com/FTBpro/go-workshop/coolfacts v0.0.0-20190430114424-99097f9b0d53 h1:U75PlbRup9ERtFo9lbZkW06h3kgEOJXKrFT7ODHmfMM= diff --git a/coolfacts-dep/exercise8/http/handler.go b/coolfacts-dep/exercise8/http/handler.go new file mode 100644 index 0000000..0e57291 --- /dev/null +++ b/coolfacts-dep/exercise8/http/handler.go @@ -0,0 +1,142 @@ +package http + +import ( + "encoding/json" + "fmt" + "html/template" + "io/ioutil" + "net/http" + + "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" +) + +type FactRepository interface { + Add(f fact.Fact) + GetAll() []fact.Fact +} + +type factsHandler struct { + factRepo FactRepository +} + +func NewFactsHandler(factRepo FactRepository) *factsHandler { + return &factsHandler{ + factRepo: factRepo, + } +} + +var newsTemplate = ` + + + Coolfacts + + + + +

Hear These Amazing Facts!

+ {{ range . }} +
+

{{.Description}}

+ +
+ {{ end }} + + + +` + +func (h *factsHandler) Ping(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "no http handler found", http.StatusNotFound) + return + } + w.Header().Add("Content-Type", "text/plain") + _, err := fmt.Fprint(w, "PONG") + if err != nil { + errMessage := fmt.Sprintf("error writing response: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + } +} + +func (h *factsHandler) Facts(w http.ResponseWriter, r *http.Request) { + if h.factRepo == nil { + http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) + } + + switch r.Method { + case http.MethodGet: + h.showFacts(w) + return + case http.MethodPost: + h.postFacts(r, w) + return + default: + http.Error(w, "no http handler found", http.StatusNotFound) + } +} + +func (h *factsHandler) postFacts(r *http.Request, w http.ResponseWriter) { + b, err := ioutil.ReadAll(r.Body) + if err != nil { + errMessage := fmt.Sprintf("error read from body: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + var req struct { + Image string `json:"image"` + Description string `json:"description"` + } + err = json.Unmarshal(b, &req) + if err != nil { + errMessage := fmt.Sprintf("error parsing fact: %v", err) + http.Error(w, errMessage, http.StatusBadRequest) + } + f := fact.Fact{ + Image: req.Image, + Description: req.Description, + } + h.factRepo.Add(f) + w.Write([]byte("SUCCESS")) +} + +func (h *factsHandler) showFacts(w http.ResponseWriter) { + w.Header().Add("Content-Type", "text/html") + tmpl, err := template.New("facts").Parse(newsTemplate) + if err != nil { + errMessage := fmt.Sprintf("error ghttp template writing: %v", err) + http.Error(w, errMessage, http.StatusInternalServerError) + return + } + tmpl.Execute(w, h.factRepo.GetAll()) +} diff --git a/coolfacts-dep/exercise8/inmem/factstore.go b/coolfacts-dep/exercise8/inmem/factstore.go new file mode 100644 index 0000000..e08433a --- /dev/null +++ b/coolfacts-dep/exercise8/inmem/factstore.go @@ -0,0 +1,22 @@ +package inmem + +import ( + "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" +) + +type factRepository struct { + facts []fact.Fact +} + +func NewFactRepository() *factRepository { + return &factRepository{} +} + +func (r *factRepository) Add(f fact.Fact) { + // append is a Go built in function + r.facts = append(r.facts, f) +} + +func (r *factRepository) GetAll() []fact.Fact { + return r.facts +} diff --git a/coolfacts-dep/exercise8/main.go b/coolfacts-dep/exercise8/main.go new file mode 100644 index 0000000..5966893 --- /dev/null +++ b/coolfacts-dep/exercise8/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "context" + "log" + "net/http" + "time" + + "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" + facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise8/http" + "github.com/FTBpro/go-workshop/coolfacts/exercise8/inmem" + "github.com/FTBpro/go-workshop/coolfacts/exercise8/mentalfloss" +) + +const ( + updateFactInterval = 5 * time.Minute +) + +func main() { + factsRepository := inmem.NewFactRepository() + handler := facthttp.NewFactsHandler(factsRepository) + + mentalflossProvider := mentalfloss.NewProvider() + service := fact.NewService(mentalflossProvider, factsRepository) + if err := service.UpdateFacts(); err != nil { + log.Fatal("couldn't update facts, try later", err.Error()) + } + + ctx, closer := context.WithCancel(context.Background()) + defer closer() + updateFactsWithTicker(ctx, service.UpdateFacts) + + http.HandleFunc("/ping", handler.Ping) + http.HandleFunc("/facts", handler.Facts) + + const port = ":9002" + log.Println("started server on http://localhost" + port) + log.Fatal(http.ListenAndServe(port, nil)) +} + +// private + +func updateFactsWithTicker(ctx context.Context, updateFunc func() error) { + tk := time.NewTicker(updateFactInterval) + go func(c context.Context) { + for { + select { + case <-tk.C: + log.Println("updating...") + if err := updateFunc(); err != nil { + log.Printf("error updating = %v", err) + } + log.Println("updated Successfully") + case <-c.Done(): + return + } + } + }(ctx) +} diff --git a/coolfacts-dep/exercise8/mentalfloss/item.go b/coolfacts-dep/exercise8/mentalfloss/item.go new file mode 100644 index 0000000..3ce677c --- /dev/null +++ b/coolfacts-dep/exercise8/mentalfloss/item.go @@ -0,0 +1,15 @@ +package mentalfloss + +import "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" + +type item struct { + FactText string `json:"fact"` + PrimaryImage string `json:"primaryImage"` +} + +func (it item) ToFact() fact.Fact { + return fact.Fact{ + Image: it.PrimaryImage, + Description: it.FactText, + } +} diff --git a/coolfacts-dep/exercise8/mentalfloss/provider.go b/coolfacts-dep/exercise8/mentalfloss/provider.go new file mode 100644 index 0000000..08cd96c --- /dev/null +++ b/coolfacts-dep/exercise8/mentalfloss/provider.go @@ -0,0 +1,58 @@ +package mentalfloss + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + + "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" +) + +const getFactsAPI = "http://mentalfloss.com/api/facts" + +type provider struct{} + +func NewProvider() *provider { + return &provider{} +} + +func (p *provider) Facts() ([]fact.Fact, error) { + log.Println("getting facts from mentalfloss") + resp, err := http.Get(getFactsAPI) + if err != nil { + return nil, fmt.Errorf("error get = %v", err) + } + defer resp.Body.Close() + + log.Println("got facts from mentalfloss successfully") + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error readAll = %v", err) + } + + facts, err := parseFromRawItems(b) + if err != nil { + return nil, fmt.Errorf("error parsing data = %v", err) + } + + return facts, nil +} + +// private + +func parseFromRawItems(b []byte) ([]fact.Fact, error) { + items := make([]item, 0) + if err := json.Unmarshal(b, &items); err != nil { + return nil, err + } + + facts := make([]fact.Fact, 0) + for _, it := range items { + newFact := it.ToFact() + facts = append(facts, newFact) + } + + return facts, nil +} diff --git a/coolfacts-dep/go.mod b/coolfacts-dep/go.mod new file mode 100644 index 0000000..13efe50 --- /dev/null +++ b/coolfacts-dep/go.mod @@ -0,0 +1,30 @@ +module github.com/FTBpro/go-workshop/coolfacts + +require ( + github.com/FTBpro/go-workshop/coolfacts/exercise1 v0.0.0 + github.com/FTBpro/go-workshop/coolfacts/exercise2 v0.0.0 + github.com/FTBpro/go-workshop/coolfacts/exercise3 v0.0.0 + github.com/FTBpro/go-workshop/coolfacts/exercise4 v0.0.0 + github.com/FTBpro/go-workshop/coolfacts/exercise5 v0.0.0 + github.com/FTBpro/go-workshop/coolfacts/exercise6 v0.0.0 + github.com/FTBpro/go-workshop/coolfacts/exercise7 v0.0.0 + github.com/FTBpro/go-workshop/coolfacts/exercise8 v0.0.0 +) + +replace github.com/FTBpro/go-workshop/coolfacts/exercise1 => ./exercise1 + +replace github.com/FTBpro/go-workshop/coolfacts/exercise2 => ./exercise2 + +replace github.com/FTBpro/go-workshop/coolfacts/exercise3 => ./exercise3 + +replace github.com/FTBpro/go-workshop/coolfacts/exercise4 => ./exercise4 + +replace github.com/FTBpro/go-workshop/coolfacts/exercise5 => ./exercise5 + +replace github.com/FTBpro/go-workshop/coolfacts/exercise6 => ./exercise6 + +replace github.com/FTBpro/go-workshop/coolfacts/exercise7 => ./exercise7 + +replace github.com/FTBpro/go-workshop/coolfacts/exercise8 => ./exercise8 + +go 1.13 diff --git a/coolfacts-dep/styles.css b/coolfacts-dep/styles.css new file mode 100644 index 0000000..a30fb11 --- /dev/null +++ b/coolfacts-dep/styles.css @@ -0,0 +1,30 @@ +/* This file is not magically included anywhere*/ +body { + font-family: Helvetica, Arial, sans-serif; + color: #26323d; + max-width: 720px; + margin: auto; +} + +article { + border: 1px solid #0095c4; + border-radius: 4px; + max-width: 320px; + text-align: center; +} + +article h3 { + font-weight: normal; +} + +article a { + color: #26323d; +} + +article a:hover { + color: #f16957; +} + +article img { + border-radius: 4px; +} From a7233b465333b125aeadc3c6b439fa16534e2cd7 Mon Sep 17 00:00:00 2001 From: Oren Rosen Date: Fri, 25 Nov 2022 17:18:59 +0200 Subject: [PATCH 04/10] remove folder --- coolfacts/cmd/main.go | 5 ----- coolfacts/go.mod | 6 +----- coolfacts/main.go | 5 +++++ 3 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 coolfacts/cmd/main.go create mode 100644 coolfacts/main.go diff --git a/coolfacts/cmd/main.go b/coolfacts/cmd/main.go deleted file mode 100644 index 16f28ba..0000000 --- a/coolfacts/cmd/main.go +++ /dev/null @@ -1,5 +0,0 @@ -package main - -func main() { - //TODO: print "Hello, World!" -} diff --git a/coolfacts/go.mod b/coolfacts/go.mod index 3fc4f15..b1dd8ae 100644 --- a/coolfacts/go.mod +++ b/coolfacts/go.mod @@ -1,7 +1,3 @@ module github.com/FTBpro/go-workshop/coolfacts -require ( - -) - -go 1.19 +go 1.18 diff --git a/coolfacts/main.go b/coolfacts/main.go new file mode 100644 index 0000000..c325f56 --- /dev/null +++ b/coolfacts/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + // TODO: Print Hello, World! +} From 7ab7d4dafb0eb672ee1327a797397f800a4f8fd6 Mon Sep 17 00:00:00 2001 From: Oren Rosen Date: Tue, 6 Dec 2022 14:04:17 +0200 Subject: [PATCH 05/10] delete stuff --- coolfacts/entrypoint/build-and-run.md | 48 ------ coolfacts/entrypoint/main.go | 9 -- coolfacts/exercise1/go.mod | 3 - coolfacts/exercise1/main.go | 28 ---- coolfacts/exercise2/fact.go | 19 --- coolfacts/exercise2/go.mod | 3 - coolfacts/exercise2/main.go | 63 -------- coolfacts/exercise3/fact.go | 19 --- coolfacts/exercise3/go.mod | 3 - coolfacts/exercise3/main.go | 100 ------------ coolfacts/exercise4/fact.go | 19 --- coolfacts/exercise4/go.mod | 3 - coolfacts/exercise4/main.go | 139 ----------------- coolfacts/exercise5/fact.go | 19 --- coolfacts/exercise5/go.mod | 3 - coolfacts/exercise5/main.go | 139 ----------------- coolfacts/exercise5/mentalfloss.go | 57 ------- coolfacts/exercise6/fact/fact.go | 19 --- coolfacts/exercise6/fact/fact_test.go | 25 --- coolfacts/exercise6/go.mod | 3 - coolfacts/exercise6/http/handler.go | 130 ---------------- coolfacts/exercise6/main.go | 40 ----- .../exercise6/mentalfloss/mentalfloss.go | 57 ------- coolfacts/exercise7/fact/fact.go | 19 --- coolfacts/exercise7/go.mod | 3 - coolfacts/exercise7/go.sum | 0 coolfacts/exercise7/http/handler.go | 130 ---------------- coolfacts/exercise7/main.go | 73 --------- .../exercise7/mentalfloss/mentalfloss.go | 57 ------- coolfacts/exercise8/fact/fact.go | 6 - coolfacts/exercise8/fact/service.go | 39 ----- coolfacts/exercise8/go.mod | 3 - coolfacts/exercise8/go.sum | 2 - coolfacts/exercise8/http/handler.go | 142 ------------------ coolfacts/exercise8/inmem/factstore.go | 22 --- coolfacts/exercise8/main.go | 59 -------- coolfacts/exercise8/mentalfloss/item.go | 15 -- coolfacts/exercise8/mentalfloss/provider.go | 58 ------- coolfacts/go.mod | 30 ---- coolfacts/styles.css | 30 ---- test/client.go | 58 ------- 41 files changed, 1694 deletions(-) delete mode 100644 coolfacts/entrypoint/build-and-run.md delete mode 100644 coolfacts/entrypoint/main.go delete mode 100644 coolfacts/exercise1/go.mod delete mode 100644 coolfacts/exercise1/main.go delete mode 100644 coolfacts/exercise2/fact.go delete mode 100644 coolfacts/exercise2/go.mod delete mode 100644 coolfacts/exercise2/main.go delete mode 100644 coolfacts/exercise3/fact.go delete mode 100644 coolfacts/exercise3/go.mod delete mode 100644 coolfacts/exercise3/main.go delete mode 100644 coolfacts/exercise4/fact.go delete mode 100644 coolfacts/exercise4/go.mod delete mode 100644 coolfacts/exercise4/main.go delete mode 100644 coolfacts/exercise5/fact.go delete mode 100644 coolfacts/exercise5/go.mod delete mode 100644 coolfacts/exercise5/main.go delete mode 100644 coolfacts/exercise5/mentalfloss.go delete mode 100644 coolfacts/exercise6/fact/fact.go delete mode 100644 coolfacts/exercise6/fact/fact_test.go delete mode 100644 coolfacts/exercise6/go.mod delete mode 100644 coolfacts/exercise6/http/handler.go delete mode 100644 coolfacts/exercise6/main.go delete mode 100644 coolfacts/exercise6/mentalfloss/mentalfloss.go delete mode 100644 coolfacts/exercise7/fact/fact.go delete mode 100644 coolfacts/exercise7/go.mod delete mode 100644 coolfacts/exercise7/go.sum delete mode 100644 coolfacts/exercise7/http/handler.go delete mode 100644 coolfacts/exercise7/main.go delete mode 100644 coolfacts/exercise7/mentalfloss/mentalfloss.go delete mode 100644 coolfacts/exercise8/fact/fact.go delete mode 100644 coolfacts/exercise8/fact/service.go delete mode 100644 coolfacts/exercise8/go.mod delete mode 100644 coolfacts/exercise8/go.sum delete mode 100644 coolfacts/exercise8/http/handler.go delete mode 100644 coolfacts/exercise8/inmem/factstore.go delete mode 100644 coolfacts/exercise8/main.go delete mode 100644 coolfacts/exercise8/mentalfloss/item.go delete mode 100644 coolfacts/exercise8/mentalfloss/provider.go delete mode 100644 coolfacts/go.mod delete mode 100644 coolfacts/styles.css delete mode 100644 test/client.go diff --git a/coolfacts/entrypoint/build-and-run.md b/coolfacts/entrypoint/build-and-run.md deleted file mode 100644 index d21bee0..0000000 --- a/coolfacts/entrypoint/build-and-run.md +++ /dev/null @@ -1,48 +0,0 @@ -# Building and Running Go Applications - -The Go compiler creates an executable that is run directly on your system. - -## Go Build - -```bash -usage: go build [-o output] [-i] [build flags] [packages] -``` - -For example, if you want to build `entrypoint`: - -```bash -$ cd coolfacts/entrypoint -$ go build . -``` - -This will create a binary called `entrypoint` in the current directory. -(`entrypoint.exe` on windows) - -```bash -$ ls -l --rwxr-xr-x [...] entrypoint --rw-r--r-- [...] main.go -``` - -And now run the binary directly on your operating system - -```bash -$ ./entrypoint -Hello World! -``` - -For development, there's an easier way -for locally running a Go application - -```bash -$ go run . -Hello World! -``` - -## Advanced - -If you want to know more, the best place to start is the output of - -```bash -$ go help build -``` diff --git a/coolfacts/entrypoint/main.go b/coolfacts/entrypoint/main.go deleted file mode 100644 index b6dc2c8..0000000 --- a/coolfacts/entrypoint/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "fmt" -) - -func main() { - fmt.Println("Welcome to the go-workshop, hope you will have fun!!") -} diff --git a/coolfacts/exercise1/go.mod b/coolfacts/exercise1/go.mod deleted file mode 100644 index 5527b1a..0000000 --- a/coolfacts/exercise1/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise1 - -go 1.13 diff --git a/coolfacts/exercise1/main.go b/coolfacts/exercise1/main.go deleted file mode 100644 index abf4066..0000000 --- a/coolfacts/exercise1/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" - "log" - "net/http" -) - -func main() { - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts/exercise2/fact.go b/coolfacts/exercise2/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts/exercise2/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts/exercise2/go.mod b/coolfacts/exercise2/go.mod deleted file mode 100644 index 9170812..0000000 --- a/coolfacts/exercise2/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise2 - -go 1.13 diff --git a/coolfacts/exercise2/main.go b/coolfacts/exercise2/main.go deleted file mode 100644 index c51354c..0000000 --- a/coolfacts/exercise2/main.go +++ /dev/null @@ -1,63 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - "net/http" -) - -func main() { - factsRepo := repo{} - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", - Description: "Did you know sonic is a hedgehog?!", - }) - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", - Description: "You won't believe what happened to Arya!", - }) - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - // We are checking that we are answering only for GET method - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "application/json") - - // Here we are getting the JSON encoding of for the all facts from the repo - b, err := json.Marshal(factsRepo.getAll()) - if err != nil { - errMessage := fmt.Sprintf("error marshaling facts : %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - // Next we are writing it the the http.ResponseWriter - _, err = w.Write(b) - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts/exercise3/fact.go b/coolfacts/exercise3/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts/exercise3/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts/exercise3/go.mod b/coolfacts/exercise3/go.mod deleted file mode 100644 index 097c981..0000000 --- a/coolfacts/exercise3/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise3 - -go 1.13 diff --git a/coolfacts/exercise3/main.go b/coolfacts/exercise3/main.go deleted file mode 100644 index ba7abaf..0000000 --- a/coolfacts/exercise3/main.go +++ /dev/null @@ -1,100 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" -) - -func main() { - factsRepo := repo{} - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", - Description: "Did you know sonic is a hedgehog?!", - }) - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", - Description: "You won't believe what happened to Arya!", - }) - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - - w.Header().Add("Content-Type", "text/plain") - - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet { - w.Header().Add("Content-Type", "application/json") - - b, err := json.Marshal(factsRepo.getAll()) - if err != nil { - errMessage := fmt.Sprintf("error marshaling facts : %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - _, err = w.Write(b) - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - } - if r.Method == http.MethodPost { - // first read the request body into a byte stream - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - // We are expecting the payload to be from the form: - // { - // "image": "image name", - // "url": "image/url", - // "description": "image description" - // } - // We will use this struct representation of the expected request body, and parse into it the data - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - - // parse the JSON-encoded data and stores the result in req - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - return - } - - // create a new fact from this request struct, and repo it using the repo - f := fact{ - Image: req.Image, - Description: req.Description, - } - - factsRepo.add(f) - w.Write([]byte("SUCCESS")) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts/exercise4/fact.go b/coolfacts/exercise4/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts/exercise4/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts/exercise4/go.mod b/coolfacts/exercise4/go.mod deleted file mode 100644 index 6a8c003..0000000 --- a/coolfacts/exercise4/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise4 - -go 1.13 diff --git a/coolfacts/exercise4/main.go b/coolfacts/exercise4/main.go deleted file mode 100644 index fdc59da..0000000 --- a/coolfacts/exercise4/main.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "log" - "net/http" -) - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func main() { - factsRepo := repo{} - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", - Description: "Did you know sonic is a hedgehog?!", - }) - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", - Description: "You won't believe what happened to Arya!", - }) - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet { - w.Header().Add("Content-Type", "text/html") - - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - facts := factsRepo.getAll() - err = tmpl.Execute(w, facts) - if err != nil { - errMessage := fmt.Sprintf("failed parse template: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - return - } - if r.Method == http.MethodPost { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - - f := fact{ - Image: req.Image, - Description: req.Description, - } - - factsRepo.add(f) - w.Write([]byte("SUCCESS")) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts/exercise5/fact.go b/coolfacts/exercise5/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts/exercise5/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts/exercise5/go.mod b/coolfacts/exercise5/go.mod deleted file mode 100644 index ada5c65..0000000 --- a/coolfacts/exercise5/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise5 - -go 1.13 diff --git a/coolfacts/exercise5/main.go b/coolfacts/exercise5/main.go deleted file mode 100644 index 1ca150a..0000000 --- a/coolfacts/exercise5/main.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "log" - "net/http" -) - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func main() { - factsRepo := repo{} - mentalflossProvider := mentalfloss{} - facts, err := mentalflossProvider.Facts() - if err != nil { - log.Fatal("can't reach mentalfloss: ", err) - } - for _, f := range facts { - factsRepo.add(f) - } - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet { - w.Header().Add("Content-Type", "text/html") - - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - facts := factsRepo.getAll() - err = tmpl.Execute(w, facts) - if err != nil { - errMessage := fmt.Sprintf("failed parse template: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - return - } - if r.Method == http.MethodPost { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - - f := fact{ - Image: req.Image, - Description: req.Description, - } - - factsRepo.add(f) - w.Write([]byte("SUCCESS")) - } - }) - - log.Println("starting server") - err = http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts/exercise5/mentalfloss.go b/coolfacts/exercise5/mentalfloss.go deleted file mode 100644 index a62a622..0000000 --- a/coolfacts/exercise5/mentalfloss.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" -) - -type mentalfloss struct{} - -func (mf mentalfloss) Facts() ([]fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get("http://mentalfloss.com/api/facts") - if err != nil { - return nil, fmt.Errorf("error making request: %v", err) - } - log.Println("got facts from mentalfloss successfully") - - // A `defer` statement defers the execution of a function until the surrounding function returns. - // This is how we make sure that we close the response body before we exit the function. - defer resp.Body.Close() - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll: %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data: %v", err) - } - - return facts, nil -} - -func parseFromRawItems(b []byte) ([]fact, error) { - var items []struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` - } - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - var facts []fact - for _, it := range items { - newFact := fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts/exercise6/fact/fact.go b/coolfacts/exercise6/fact/fact.go deleted file mode 100644 index 2e15ecc..0000000 --- a/coolfacts/exercise6/fact/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package fact - -type Fact struct { - Image string - Description string -} - -type Repository struct { - facts []Fact -} - -func (r *Repository) Add(f Fact) { - // append is a Go built in function - r.facts = append(r.facts, f) -} - -func (r *Repository) GetAll() []Fact { - return r.facts -} diff --git a/coolfacts/exercise6/fact/fact_test.go b/coolfacts/exercise6/fact/fact_test.go deleted file mode 100644 index 7d76ecc..0000000 --- a/coolfacts/exercise6/fact/fact_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package fact - -import ( - "testing" -) - -func TestRepo_Add(t *testing.T) { - repo := Repository{} - fact := Fact{ - Image: "img", - Description: "desc", - } - repo.Add(fact) - - gotFacts := repo.GetAll() - var hasItem bool - for _, gotFact := range gotFacts { - if gotFact.Image == fact.Image && gotFact.Description == fact.Description { - hasItem = true - } - } - if !hasItem { - t.Errorf("can't find added fact") - } -} diff --git a/coolfacts/exercise6/go.mod b/coolfacts/exercise6/go.mod deleted file mode 100644 index 2cbafe9..0000000 --- a/coolfacts/exercise6/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise6 - -go 1.13 diff --git a/coolfacts/exercise6/http/handler.go b/coolfacts/exercise6/http/handler.go deleted file mode 100644 index dfd8598..0000000 --- a/coolfacts/exercise6/http/handler.go +++ /dev/null @@ -1,130 +0,0 @@ -package http - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" -) - -type FactsHandler struct { - FactRepo *fact.Repository -} - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func (h *FactsHandler) Ping(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } -} - -func (h *FactsHandler) Facts(w http.ResponseWriter, r *http.Request) { - if h.FactRepo == nil { - http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) - } - - switch r.Method { - case http.MethodGet: - h.showFacts(w) - return - case http.MethodPost: - h.postFacts(r, w) - return - default: - http.Error(w, "no http handler found", http.StatusNotFound) - } -} - -func (h *FactsHandler) postFacts(r *http.Request, w http.ResponseWriter) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - f := fact.Fact{ - Image: req.Image, - Description: req.Description, - } - h.FactRepo.Add(f) - w.Write([]byte("SUCCESS")) -} - -func (h *FactsHandler) showFacts(w http.ResponseWriter) { - w.Header().Add("Content-Type", "text/html") - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - tmpl.Execute(w, h.FactRepo.GetAll()) -} diff --git a/coolfacts/exercise6/main.go b/coolfacts/exercise6/main.go deleted file mode 100644 index dd8c193..0000000 --- a/coolfacts/exercise6/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" - facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise6/http" - "github.com/FTBpro/go-workshop/coolfacts/exercise6/mentalfloss" -) - -func main() { - factsRepo := fact.Repository{} - handlerer := facthttp.FactsHandler{ - FactRepo: &factsRepo, - } - - mentalflossProvider := mentalfloss.Mentalfloss{} - err := updateFacts(mentalflossProvider, &factsRepo) - - http.HandleFunc("/ping", handlerer.Ping) - http.HandleFunc("/facts", handlerer.Facts) - - log.Println("starting server") - err = http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} - -func updateFacts(mf mentalfloss.Mentalfloss, factsRepo *fact.Repository) error { - facts, err := mf.Facts() - if err != nil { - log.Fatal("can't reach mentalfloss: ", err) - } - for _, f := range facts { - factsRepo.Add(f) - } - return err -} diff --git a/coolfacts/exercise6/mentalfloss/mentalfloss.go b/coolfacts/exercise6/mentalfloss/mentalfloss.go deleted file mode 100644 index 04923d3..0000000 --- a/coolfacts/exercise6/mentalfloss/mentalfloss.go +++ /dev/null @@ -1,57 +0,0 @@ -package mentalfloss - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" -) - -type Mentalfloss struct { -} - -func (mf Mentalfloss) Facts() ([]fact.Fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get("http://mentalfloss.com/api/facts") - if err != nil { - return nil, fmt.Errorf("error making request: %v", err) - } - defer resp.Body.Close() - log.Println("got facts from mentalfloss successfully") - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll: %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data: %v", err) - } - - return facts, nil -} - -func parseFromRawItems(b []byte) ([]fact.Fact, error) { - var items []struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` - } - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - var facts []fact.Fact - for _, it := range items { - newFact := fact.Fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts/exercise7/fact/fact.go b/coolfacts/exercise7/fact/fact.go deleted file mode 100644 index 0b3485d..0000000 --- a/coolfacts/exercise7/fact/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package fact - -type Fact struct { - Image string - Description string -} - -type Repository struct { - facts []Fact -} - -func (r *Repository) Add(f Fact) { - // append is a Go built in function - r.facts = append([]Fact{f}, r.facts...) -} - -func (r *Repository) GetAll() []Fact { - return r.facts -} diff --git a/coolfacts/exercise7/go.mod b/coolfacts/exercise7/go.mod deleted file mode 100644 index 75ed072..0000000 --- a/coolfacts/exercise7/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise7 - -go 1.12 diff --git a/coolfacts/exercise7/go.sum b/coolfacts/exercise7/go.sum deleted file mode 100644 index e69de29..0000000 diff --git a/coolfacts/exercise7/http/handler.go b/coolfacts/exercise7/http/handler.go deleted file mode 100644 index af02f38..0000000 --- a/coolfacts/exercise7/http/handler.go +++ /dev/null @@ -1,130 +0,0 @@ -package http - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" -) - -type FactsHandler struct { - FactRepo *fact.Repository -} - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func (h *FactsHandler) Ping(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } -} - -func (h *FactsHandler) Facts(w http.ResponseWriter, r *http.Request) { - if h.FactRepo == nil { - http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) - } - - switch r.Method { - case http.MethodGet: - h.showFacts(w) - return - case http.MethodPost: - h.postFacts(r, w) - return - default: - http.Error(w, "no http handler found", http.StatusNotFound) - } -} - -func (h *FactsHandler) postFacts(r *http.Request, w http.ResponseWriter) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - f := fact.Fact{ - Image: req.Image, - Description: req.Description, - } - h.FactRepo.Add(f) - w.Write([]byte("SUCCESS")) -} - -func (h *FactsHandler) showFacts(w http.ResponseWriter) { - w.Header().Add("Content-Type", "text/html") - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - tmpl.Execute(w, h.FactRepo.GetAll()) -} diff --git a/coolfacts/exercise7/main.go b/coolfacts/exercise7/main.go deleted file mode 100644 index 16d6414..0000000 --- a/coolfacts/exercise7/main.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "context" - "log" - "net/http" - "time" - - "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" - facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise7/http" - "github.com/FTBpro/go-workshop/coolfacts/exercise7/mentalfloss" -) - -const ( - updateFactInterval = time.Minute * 1 -) - -func main() { - factsRepo := fact.Repository{} - handlerer := facthttp.FactsHandler{ - FactRepo: &factsRepo, - } - - mentalflossProvider := mentalfloss.Mentalfloss{} - updateFunc := updateFactsFunc(mentalflossProvider, &factsRepo) - if err := updateFunc(); err != nil { - log.Fatal(err) - } - - ctx, closer := context.WithCancel(context.Background()) - defer closer() - updateFactsWithTicker(ctx, updateFunc) - - http.HandleFunc("/ping", handlerer.Ping) - http.HandleFunc("/facts", handlerer.Facts) - - const port = ":9002" - log.Println("started server on http://localhost" + port) - log.Fatal(http.ListenAndServe(port, nil)) -} - -// private - -func updateFactsWithTicker(ctx context.Context, updateFunc func() error) { - tk := time.NewTicker(updateFactInterval) - go func(c context.Context) { - for { - select { - case <-tk.C: - log.Println("updating...") - if err := updateFunc(); err != nil { - log.Printf("error updating = %v", err) - } - log.Println("updated Successfully") - case <-c.Done(): - return - } - } - }(ctx) -} - -func updateFactsFunc(mf mentalfloss.Mentalfloss, factsRepo *fact.Repository) func() error { - return func() error { - facts, err := mf.Facts() - if err != nil { - log.Fatal("can't reach mentalfloss: ", err) - } - for _, f := range facts { - factsRepo.Add(f) - } - return err - } -} diff --git a/coolfacts/exercise7/mentalfloss/mentalfloss.go b/coolfacts/exercise7/mentalfloss/mentalfloss.go deleted file mode 100644 index c3e127a..0000000 --- a/coolfacts/exercise7/mentalfloss/mentalfloss.go +++ /dev/null @@ -1,57 +0,0 @@ -package mentalfloss - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" -) - -type Mentalfloss struct { -} - -func (mf Mentalfloss) Facts() ([]fact.Fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get("http://mentalfloss.com/api/facts") - if err != nil { - return nil, fmt.Errorf("error making request: %v", err) - } - defer resp.Body.Close() - log.Println("got facts from mentalfloss successfully") - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll: %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data: %v", err) - } - - return facts, nil -} - -func parseFromRawItems(b []byte) ([]fact.Fact, error) { - var items []struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` - } - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - var facts []fact.Fact - for _, it := range items { - newFact := fact.Fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts/exercise8/fact/fact.go b/coolfacts/exercise8/fact/fact.go deleted file mode 100644 index 65d33de..0000000 --- a/coolfacts/exercise8/fact/fact.go +++ /dev/null @@ -1,6 +0,0 @@ -package fact - -type Fact struct { - Image string - Description string -} diff --git a/coolfacts/exercise8/fact/service.go b/coolfacts/exercise8/fact/service.go deleted file mode 100644 index 37a82e2..0000000 --- a/coolfacts/exercise8/fact/service.go +++ /dev/null @@ -1,39 +0,0 @@ -package fact - -import ( - "fmt" -) - -type Provider interface { - Facts() ([]Fact, error) -} - -type Repository interface { - Add(f Fact) - GetAll() []Fact -} - -type service struct { - provider Provider - repo Repository -} - -func NewService(p Provider, s Repository) *service { - return &service{ - provider: p, - repo: s, - } -} - -func (s *service) UpdateFacts() error { - facts, err := s.provider.Facts() - if err != nil { - return fmt.Errorf("fact.service.UpdateFacts failed returiev facts %v", err) - } - - for _, fact := range facts { - s.repo.Add(fact) - } - - return nil -} diff --git a/coolfacts/exercise8/go.mod b/coolfacts/exercise8/go.mod deleted file mode 100644 index 970b0a1..0000000 --- a/coolfacts/exercise8/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise8 - -go 1.13 diff --git a/coolfacts/exercise8/go.sum b/coolfacts/exercise8/go.sum deleted file mode 100644 index c7519f2..0000000 --- a/coolfacts/exercise8/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/FTBpro/go-workshop v0.0.0-20190430114424-99097f9b0d53 h1:JEwdzoVkee+6JmnlW4co6UWKtXpEDLnwcjaIVXe3gXQ= -github.com/FTBpro/go-workshop/coolfacts v0.0.0-20190430114424-99097f9b0d53 h1:U75PlbRup9ERtFo9lbZkW06h3kgEOJXKrFT7ODHmfMM= diff --git a/coolfacts/exercise8/http/handler.go b/coolfacts/exercise8/http/handler.go deleted file mode 100644 index 0e57291..0000000 --- a/coolfacts/exercise8/http/handler.go +++ /dev/null @@ -1,142 +0,0 @@ -package http - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" -) - -type FactRepository interface { - Add(f fact.Fact) - GetAll() []fact.Fact -} - -type factsHandler struct { - factRepo FactRepository -} - -func NewFactsHandler(factRepo FactRepository) *factsHandler { - return &factsHandler{ - factRepo: factRepo, - } -} - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - - -` - -func (h *factsHandler) Ping(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } -} - -func (h *factsHandler) Facts(w http.ResponseWriter, r *http.Request) { - if h.factRepo == nil { - http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) - } - - switch r.Method { - case http.MethodGet: - h.showFacts(w) - return - case http.MethodPost: - h.postFacts(r, w) - return - default: - http.Error(w, "no http handler found", http.StatusNotFound) - } -} - -func (h *factsHandler) postFacts(r *http.Request, w http.ResponseWriter) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - f := fact.Fact{ - Image: req.Image, - Description: req.Description, - } - h.factRepo.Add(f) - w.Write([]byte("SUCCESS")) -} - -func (h *factsHandler) showFacts(w http.ResponseWriter) { - w.Header().Add("Content-Type", "text/html") - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - tmpl.Execute(w, h.factRepo.GetAll()) -} diff --git a/coolfacts/exercise8/inmem/factstore.go b/coolfacts/exercise8/inmem/factstore.go deleted file mode 100644 index e08433a..0000000 --- a/coolfacts/exercise8/inmem/factstore.go +++ /dev/null @@ -1,22 +0,0 @@ -package inmem - -import ( - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" -) - -type factRepository struct { - facts []fact.Fact -} - -func NewFactRepository() *factRepository { - return &factRepository{} -} - -func (r *factRepository) Add(f fact.Fact) { - // append is a Go built in function - r.facts = append(r.facts, f) -} - -func (r *factRepository) GetAll() []fact.Fact { - return r.facts -} diff --git a/coolfacts/exercise8/main.go b/coolfacts/exercise8/main.go deleted file mode 100644 index 5966893..0000000 --- a/coolfacts/exercise8/main.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "context" - "log" - "net/http" - "time" - - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" - facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise8/http" - "github.com/FTBpro/go-workshop/coolfacts/exercise8/inmem" - "github.com/FTBpro/go-workshop/coolfacts/exercise8/mentalfloss" -) - -const ( - updateFactInterval = 5 * time.Minute -) - -func main() { - factsRepository := inmem.NewFactRepository() - handler := facthttp.NewFactsHandler(factsRepository) - - mentalflossProvider := mentalfloss.NewProvider() - service := fact.NewService(mentalflossProvider, factsRepository) - if err := service.UpdateFacts(); err != nil { - log.Fatal("couldn't update facts, try later", err.Error()) - } - - ctx, closer := context.WithCancel(context.Background()) - defer closer() - updateFactsWithTicker(ctx, service.UpdateFacts) - - http.HandleFunc("/ping", handler.Ping) - http.HandleFunc("/facts", handler.Facts) - - const port = ":9002" - log.Println("started server on http://localhost" + port) - log.Fatal(http.ListenAndServe(port, nil)) -} - -// private - -func updateFactsWithTicker(ctx context.Context, updateFunc func() error) { - tk := time.NewTicker(updateFactInterval) - go func(c context.Context) { - for { - select { - case <-tk.C: - log.Println("updating...") - if err := updateFunc(); err != nil { - log.Printf("error updating = %v", err) - } - log.Println("updated Successfully") - case <-c.Done(): - return - } - } - }(ctx) -} diff --git a/coolfacts/exercise8/mentalfloss/item.go b/coolfacts/exercise8/mentalfloss/item.go deleted file mode 100644 index 3ce677c..0000000 --- a/coolfacts/exercise8/mentalfloss/item.go +++ /dev/null @@ -1,15 +0,0 @@ -package mentalfloss - -import "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" - -type item struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` -} - -func (it item) ToFact() fact.Fact { - return fact.Fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } -} diff --git a/coolfacts/exercise8/mentalfloss/provider.go b/coolfacts/exercise8/mentalfloss/provider.go deleted file mode 100644 index 08cd96c..0000000 --- a/coolfacts/exercise8/mentalfloss/provider.go +++ /dev/null @@ -1,58 +0,0 @@ -package mentalfloss - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" -) - -const getFactsAPI = "http://mentalfloss.com/api/facts" - -type provider struct{} - -func NewProvider() *provider { - return &provider{} -} - -func (p *provider) Facts() ([]fact.Fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get(getFactsAPI) - if err != nil { - return nil, fmt.Errorf("error get = %v", err) - } - defer resp.Body.Close() - - log.Println("got facts from mentalfloss successfully") - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll = %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data = %v", err) - } - - return facts, nil -} - -// private - -func parseFromRawItems(b []byte) ([]fact.Fact, error) { - items := make([]item, 0) - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - facts := make([]fact.Fact, 0) - for _, it := range items { - newFact := it.ToFact() - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts/go.mod b/coolfacts/go.mod deleted file mode 100644 index 13efe50..0000000 --- a/coolfacts/go.mod +++ /dev/null @@ -1,30 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts - -require ( - github.com/FTBpro/go-workshop/coolfacts/exercise1 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise2 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise3 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise4 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise5 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise6 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise7 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise8 v0.0.0 -) - -replace github.com/FTBpro/go-workshop/coolfacts/exercise1 => ./exercise1 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise2 => ./exercise2 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise3 => ./exercise3 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise4 => ./exercise4 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise5 => ./exercise5 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise6 => ./exercise6 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise7 => ./exercise7 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise8 => ./exercise8 - -go 1.13 diff --git a/coolfacts/styles.css b/coolfacts/styles.css deleted file mode 100644 index a30fb11..0000000 --- a/coolfacts/styles.css +++ /dev/null @@ -1,30 +0,0 @@ -/* This file is not magically included anywhere*/ -body { - font-family: Helvetica, Arial, sans-serif; - color: #26323d; - max-width: 720px; - margin: auto; -} - -article { - border: 1px solid #0095c4; - border-radius: 4px; - max-width: 320px; - text-align: center; -} - -article h3 { - font-weight: normal; -} - -article a { - color: #26323d; -} - -article a:hover { - color: #f16957; -} - -article img { - border-radius: 4px; -} diff --git a/test/client.go b/test/client.go deleted file mode 100644 index 2f0d6b7..0000000 --- a/test/client.go +++ /dev/null @@ -1,58 +0,0 @@ -package test - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "net/http" - "testing" - - "github.com/gin-gonic/gin/json" -) - -type Client struct { - ServerURL string -} - -func (c Client) Do(t *testing.T, method, path string, body interface{}) *http.Response { - t.Helper() - var bodyReader io.Reader - if body != nil { - bodyJ, err := json.Marshal(body) - if err != nil { - t.Fatalf("bug in test %s: error marshaling body", t.Name()) - } - bodyReader = bytes.NewReader(bodyJ) - } - req, err := http.NewRequest(method, c.ServerURL+path, bodyReader) - if err != nil { - t.Fatalf("bug in test %s: error preparing request", t.Name()) - } - resp, err := http.DefaultClient.Do(req) - if err != nil { - t.Fatalf("error from http client: %s", err) - } - return resp -} - -func (c Client) Ping(t *testing.T) { - resp := c.Do(t, "GET", "/ping", nil) - if resp.StatusCode != http.StatusOK { - t.Errorf("ping should return status code 200 OK, but got %d", resp.StatusCode) - } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("error reading response body: %s", body) - } - defer func() { - err := resp.Body.Close() - if err != nil { - t.Fatalf("error closing response body: %s", err) - } - }() - - if string(body) != "PONG" { - t.Error(fmt.Sprintf("expected `PONG`, got `%s`", string(body))) - } -} From 7c92f9eb75bfb9fe13d8d888ac254152fe3fe25e Mon Sep 17 00:00:00 2001 From: Oren Rosen Date: Tue, 6 Dec 2022 14:27:43 +0200 Subject: [PATCH 06/10] Add docs --- coolfacts/docs/ex1.md | 45 ++++ coolfacts/docs/ex2-ping.md | 185 ++++++++++++++ coolfacts/docs/ex3-get-facts.md | 303 ++++++++++++++++++++++ coolfacts/docs/ex4-initial-client.md | 161 ++++++++++++ coolfacts/docs/ex5-create-fact.md | 359 +++++++++++++++++++++++++++ coolfacts/docs/ex6-search-facts.md | 172 +++++++++++++ coolfacts/docs/ex7-basic-router.md | 70 ++++++ 7 files changed, 1295 insertions(+) create mode 100644 coolfacts/docs/ex1.md create mode 100644 coolfacts/docs/ex2-ping.md create mode 100644 coolfacts/docs/ex3-get-facts.md create mode 100644 coolfacts/docs/ex4-initial-client.md create mode 100644 coolfacts/docs/ex5-create-fact.md create mode 100644 coolfacts/docs/ex6-search-facts.md create mode 100644 coolfacts/docs/ex7-basic-router.md diff --git a/coolfacts/docs/ex1.md b/coolfacts/docs/ex1.md new file mode 100644 index 0000000..ed4d4da --- /dev/null +++ b/coolfacts/docs/ex1.md @@ -0,0 +1,45 @@ +# Part 1 +###Create "Hello, World!" Application + +In this exercise, you will create a basic "Hello, World!" application. You will learn how to build and run a Go program and basic terminology. + +## The Starting Point +TODO: add pre requirements +To get the initial application, run the next command: +```commandline +$ git clone --branch v1-hello-world https://github.com/FTBpro/go-workshop.git +``` +This will clone the first branch which we will use for building and running our very first Go application. +The application will simply print "Hello, World!". Go on and navigate to `go-workshop/coolfacts` directory. You should be on branch `v1-hello-world`. + +## Your Goal +Execute a Go program that prints to the screen "Hello, World". Understand terms like packages, module, main, and get to know basic Go commands. + +## Step 1 +When looking around the program, you will notice two files inside the folder `coolfacts` +- main.go +- go.mod + +### main.go +The _main package_ is the starting point for every Go application. A _package_ in go is a way to organize and group functions, and it is nothing but a directory inside your Go workspace, containing one or more Go source files or other packages. Go package name usually is the same as the directory name. package main is almost the only package that usually isn't like the folder name. + +In Go, the _main package_ is a special package which is used with the executable commands. The `main()` function is a special type of function and it is the entry point of the executable program. When running the application, Go will automatically call `main()` + +In every source file, the first line is the package name. We see here the first line is +```go +package main +``` + +### go.mod +`go.mod` file is the root of a Go module. Go module is a collection of packages, stored in a file tree. Go code is grouped into packages, and packages are grouped into modules. Your module specifies dependencies needed to run your code, including the Go version and the set of other modules it requires. + +The first line in the `go.mod` defines the module's path, which is also the import path. + +In this example, you can see the module's path is +```goregexp +github.com/FTBpro/go-workshop/coolfacts +``` +The module's path is the import for consumers of this module. When importing for the first time, Go will fetch the module from the specified path. + +Currently, our module has no dependencies. + diff --git a/coolfacts/docs/ex2-ping.md b/coolfacts/docs/ex2-ping.md new file mode 100644 index 0000000..40ff845 --- /dev/null +++ b/coolfacts/docs/ex2-ping.md @@ -0,0 +1,185 @@ +# Part 2 + +In this exercise, you will create a simple server that answering to `/ping`. + +## The Starting Point +To get the initial application, run the next command: +```commandline +$ git clone --branch v2-initial-server-ping https://github.com/FTBpro/go-workshop.git +``` +This will clone the branch `v2-initial-server-ping` which we will use for building and running our web server. + +## Your Goal + +After implementing all the TODOs, you will have an application that runs a server that answer PONG to ping. + +For every other path, the server will return not-found response with the following JSON: +```json +{ + "error": "path wasn't found" +} +``` + +## Getting Started +Take a look around the program. You will notice one folder - `cmd` + +In Go programs, `cmd` folder is convention for having more than one binary (e.g application). +Usually the directory name inside cmd matches the binary name, and each folder has package `main`. In this exercise we only have one application, but in the next ones, we will also have client application. + +Inside the `cmd` we have folder `coolfact_server`. This folder includes `main` package, with two files: +- `main.go`: file that includes our `main()` function +- `server.go`: this is the transport of the application. In this file we will implement the http handlers for the server APIs. + +## Notice `main.go` + +Let's have a look at `coolfacts_server/main.go`: +```go +package main + +import ( + "fmt" + "log" + "net/http" +) + +func main() { + fmt.Println("Hello, Server!") + + server := NewServer() + + log.Println("starting server on port 9002") + log.Println("You can go to http://127.0.0.1:9002/ping") + if err := http.ListenAndServe(":9002", server); err != nil { + panic(fmt.Errorf("server crashed! err: %w", err)) + } +} +``` +We first can notice the imports `"fmt"`, `"log"` and `"net/http"`. These are standard Go libraries. + +Inside of the `main()` function, we're initializing the server, and sending it to `http.ListenAndserve` method. +In Go, a package name is only the last param, and each type in Go has a name composed of the package name and the type identifier. Although the import path is `net/http`, the package name is `http`, and the identifier is `ListenAndServe`. + +The method `http.ListenAndServe` receives an address and an `http.Handler`: +- address: This is the address it will listen on the TCP network. +- `http.Handler`: For every incoming connection, it will use the `http.Handler` to handle the requests, which defined as: + ```go + package http + + type Handler interface { + ServeHTTP(w ResponseWriter, r *Request) + } + ``` + The handler should handle the incoming `http.Request` and write the response to the `ResponseWriter`. In our application the http.Handler will be the `server`. + +What you will have to complete is: + +## Step 1 - `cmd/server.go` +The server is responsible to respond to the incoming HTTP request, it will have `/ping` route, for answering with PONG. + +You can notice that the server has method `ServeHTTP`. This is the entry point for every incoming HTTP request. The server implement `http.Handler`. We have switch case according to the HTTP method and path. + +The only recognized path is `GET /ping`. On every other path it returns `HandleNotFound` which you will implement. In real world applications, we will usually use a framework wrapping Go http which will provide us with more advanced routing mechanism. + +- Finish implement `NewServer` function. +- Implement `HandlePing` method. + - Response with status 200 (const `http.StatusOK`) + - Write `PONG` as the response. + - Clue 1 - check the `http.ResponseWriter` interface. Notice its methods and decide what you can use. + - Clue 2 - The `http.ResponseWriter` implements `io.Writer` (check why). Therefore, we can use a method in the Go package `fmt` to write the response +- Implement `HandleNotFound` and `HandleError` methods + - The JSON response for this methods should be the same: + ```json + { + "error": + } + ``` + - In case of not found return status 404 (Not Found Error), in case of other error, return status 500 (Internal Server Error) + +### Building and Running +The main package of our server-application lives inside coolfacts/cmd/coolfacts_service.go, but the go.mod is under coolfacts directory. +The go build must be called from the root of the module, in here it will be called from folder coolfacts. The path we will pass to go build will be the path to the main we want to run: +```commandline +.../coolfacts$ go build ./cmd/coolfacts_server/. +``` +This command will create executable name coolfacts_server which we can run by `./coolfacts_server` + +Alternatively we can build and run in one single command: +```commandline +.../coolfacts$ go run ./cmd/coolfacts_server/. +``` + +If everything is implemented, this is what the final result should look like: (TODO:oren): not found path in gif +![factsgif](https://user-images.githubusercontent.com/5252381/204143457-6eaf59d3-6c52-4fbb-8d2a-19d22436cbd8.gif) + +# Full walkthrough +In the following section you fill find a full walk through. Use it in case you are stuck + +## Step 1 - The HTTP transport layer (server) +We implement `NewServer`, simply by returning a reference to the `service` +```go +type server struct { +} + +func NewServer(service Service) *server { +return &server{} +} +``` + +###`HandlePing` method: +```go +func (s *server) HandlePing(w http.ResponseWriter) { +w.WriteHeader(http.StatusOK) + +if _, err := fmt.Fprint(w, "PONG"); err != nil { +fmt.Printf("ERROR writing to ResponseWriter: %s\n", err) +return +} +} +``` +First, we need to write the status. For this we use `w.WriteHeader(http.StatusOK)` + +Next, we will write “PONG”, We notice that the `http.ResponseWriter` implements `io.Writer` +Hence we can use all bunch of methods for writing the response. Here we only need to return text, so we will use `fmt.Fprintf` which receives `io.Writer`. + +### `HandleNotFound` and `HandleError`: +These methods are almost identical, except of the status. `HandleNotFound` returns status 404 - `http.StatusNotFound` +And `HandleError` returns status 500 - `http.StatusInternalServerError` +```go +func (s *server) HandleNotFound(w http.ResponseWriter, err error) { +w.WriteHeader(http.StatusNotFound) +w.Header().Set("Content-Type", "application/json") + +response := map[string]interface{}{ +"error": err, +} + +if err := json.NewEncoder(w).Encode(response); err != nil { +fmt.Printf("HandleGetFacts ERROR writing response: %s", err) +} +} + +func (s *server) HandleError(w http.ResponseWriter, err error) { +w.WriteHeader(http.StatusInternalServerError) +w.Header().Set("Content-Type", "application/json") + +response := map[string]interface{}{ +"error": err, +} + +if err := json.NewEncoder(w).Encode(response); err != nil { +fmt.Printf("HandleGetFacts ERROR writing response: %s", err) +} +} +``` + +Since we are returning a JSON, we can use the Go `json` package, which can write a JSON encoding of the response to the writer. +The `NewEncoder` method receives `io.Writer`. We already can notice the power of Go interfaces, and especially interfaces with one method. +We can send the `http.ResponseWriter` to different packages methods, and use a different encoding, and the packages are totally agnostic to HTTP. + + +# Finish! + + +Congratulation! You've just implemented a web server and BL for returning facts. + +In the following exercise we will add an API for creating a fact, and a client application for calling our server. diff --git a/coolfacts/docs/ex3-get-facts.md b/coolfacts/docs/ex3-get-facts.md new file mode 100644 index 0000000..7d5c5d7 --- /dev/null +++ b/coolfacts/docs/ex3-get-facts.md @@ -0,0 +1,303 @@ +# Part 3 + +In this exercise, you will add an API to the server for presenting the existing facts. + +## The Starting Point +To get the initial application, run the next command: +```commandline +$ git clone --branch v3-server-get-facts https://github.com/FTBpro/go-workshop.git +``` +This will clone the branch `v3-server-get-facts` which we will use for building and running our web server. + +## Your Goal + +After implementing all the TODOs, the server will export another API for getting the current facts. +```json +GET /facts + +response: +{ + "facts": [ + { + "image": "...", + "description": "..." + } + //... + ] +} +``` + +## Getting Started +Take a look around the program. You will notice new folders - `coolfact` and `inmem`. In addition you will notice some changes in the main package. + +### coolfact +This is a package containing the entity `fact` and the service for implementing the use case (business logic). +In this application we won't have much BL, if any. Our application is a very simple CRUD app, and the service will mainly call the repo as we will see. + +### inmem +In this package we will implement the facts-repository. We will use in memory. +In Go, packages names are very important. A package name should say what it provides, not what it contains. +We won't have packages names like `repos`, `models`, `utils`, `common`, etc. + +A package’s name provides context for its contents, making it easier for clients to understand what the package is for and how to use it. +For example, Go package `time` indicates that it contains functionality for handling times, probably a struct and behaviour for measuring and displaying time. Go package `http` lets you speak `HTTP`. + +In our case, package `inmem` imply that it uses memory mechanism. If we have SQL database, we will have package `sql` that implies it lets you speak SQL. + +## Step 0 - notice changes in main.go +Let's have a look at `coolfacts_server/main.go`: +```go +package main + +import ( + "fmt" + "log" + "net/http" + + // new imports + "github.com/FTBpro/go-workshop/coolfacts/coolfact" + "github.com/FTBpro/go-workshop/coolfacts/inmem" +) + +func main() { + fmt.Println("Hello, Server!") + + // new initializations + factsRepo := inmem.NewFactsRepository(seedFacts()...) + service := coolfact.NewService(factsRepo) + server := NewServer(service) + + log.Println("starting server on port 9002") + log.Println("You can go to http://127.0.0.1:9002/ping") + if err := http.ListenAndServe(":9002", server); err != nil { + panic(fmt.Errorf("server crashed! err: %w", err)) + } +} + +func seedFacts() []coolfact.Fact { + return []coolfact.Fact{ + { + Topic: "Games", + Description: "Did you know sonic is a hedgehog?!", + }, + { + Topic: "TV", + Description: "You won't believe what happened to Arya!", + }, + } +} +``` +We first can notice the new imports: +```go +"github.com/FTBpro/go-workshop/coolfacts/coolfact" +"github.com/FTBpro/go-workshop/coolfacts/inmem" +``` +we are importing packages from our own module. We have the module path and then the path to the package we want to import. The module path is `github.com/FTBpro/go-workshop/coolfacts`. + +In the next lines we initializing the repo, service and the server. A pacage name is only the last param, and each type in Go has a name composed from the package name and the type identifier. For example, we call `inmem.NewFactsRepository`. The package name is `inmem`, and the type identifier is `inmem.NewFactsRepository()`. + +You can note that we are initialzing the repo with facts: `inmem.NewFactsRepository(seedFacts()...)`. The `inmem.NewFactsRepository` signature is: +```go +func NewFactsRepository(facts ...coolfact.Fact) *factsRepo { +``` +The 3 dots indicates that it is a _variadic_ function. It can be called with any number of trailing arguments. You might already notices that `fmt.Println` is a variadic function: +```go +func Println(a ...any) (n int, err error) { +``` + +If you already have multiple args in a slice, you apply them to a variadic function using `func(slice...)`. This is why we call the function like this: +```go +inmem.NewFactsRepository(seedFacts()...) +``` + + +What you will have to complete is: +## Step 1 - package `coolfact` +This package handles the BL of the application. + +### file `coolfact/fact.go`: +In here we have the entity of the application. A struct named `Fact`. +- Complete the definition of `Fact`. It should have the following fields: + - Image string + - Description string + +### file `coolfact/service.go`: +In service.go we have the service which will handle the "BL" for the application. In the service you will: +- **Finish `Repository` interface{}** + - in Go we declare interfaces where we use them, not where we implement them. For the service to operate properly, it requires a `Repository` interface which he defines. It makes sense, since the service knows what it needs to do, and what the dependancy it needs to have. We can note that the name of the interface{} isn't InmemRepo or SQLRepo or something else, since the service is agnostic to the way the repo operates. He doesn't care about the mechanism, only behaviour. + - Add one method for getting facts. + - `GetFacts` - return slice of `coolfact.Fact` and an error + - Finish definition of service. + - Implement `NewService`. Return instance of `service` initialized with its field. + - Implement methods `GetFacts`. + +## Step 2 - Package `inmem` +### file `inmem/factsrepo.go`: +Here we will implement the facts repository. Currently, only with functionality to return facts. +- Implement `NewFactsRepository`. +- Implement method `GetFacts`. + +## Step 3 - `cmd/server.go` +As mentioned before, you will implement a new API for the `server`, what you will have to complete in the server is: +- Finish definition of the FactsService interface. Which method does the server needs in order to operate? +- Add field in the `server` for the factsService, and complete the initialization. In function `NewServer` add pass argument. +- In method `serveHTTP`, add a case for the new API, that will call method `HandleGetFacts` +- Implement `HandleGetFacts` method. The method for handling the `GET /facts` API + - Call the service in order to get the facts. + - Format the response to JSON: + ```json + { + "facts": [ + { + "image": "...", + "description": "..." + }, + //... + ] + } + ``` + - Write status 200. + - Set "content-type" header to "application/json". + +### Building and Running + +If everything is implemented well, this is what the final result should look like when running the program: +![factsgif](https://user-images.githubusercontent.com/5252381/204143457-6eaf59d3-6c52-4fbb-8d2a-19d22436cbd8.gif) + +# Full walkthrough +In the following section you fill find a full walkthrough. Use it in case you are stuck. + +## Step 1 - Implement the core entity and the service + +In `fact.go` we simply add fiedls to the entity: +```go +type Fact struct { + Image string + Description string +} +``` +We can notice that these are exported (public) fields, since it’s an entity and the rest of the application should be aware of. + +#### In `service.go` +The `Repository` interface currently only should have one method for getting facts. +The `service` has one field for the `factsRepo`, notice that the service and the field are _private_, we don’t want that someone will set or get it from outside. +We want to require the consumer to use the initializer. +```go +type Repository interface { + GetFacts() ([]Fact, error) +} + +type service struct { + factsRepo Repository +} + +func NewService(factsRepo Repository) *service { + return &service{ + factsRepo: factsRepo, + } +} +``` + +And the `GetFacts` implementation: +```go +func (s *service) GetFacts() ([]Fact, error) { + facts, err := s.factsRepo.GetFacts() + if err != nil { + return nil, fmt.Errorf("factsService.GetFacts: %w", err) + } + + return facts, nil +} +``` +In GetFacts the service calls the repo. Notice that if there is an error, the service wraps it and adding some context, so we will have friendlier message. + +## Step 2 - The repo +We initialize the `factsRepo` with the arg slice: +```go +func NewFactsRepository(facts ...coolfact.Fact) *factsRepo { + return &factsRepo{ + facts: facts, + } +} + +func (r *factsRepo) GetFacts() ([]coolfact.Fact, error) { + return r.facts, nil +} +``` + +## Step 3 - The HTTP transport layer +The `server` receives the `Service interface{}` which implements the BL. We're injecting `Service` interface as a dependency to the `server`, for the `server` to operate. This is what the `server` requires. +```go +type FactsService interface { + GetFacts() ([]coolfact.Fact, error) +} + +type server struct { + factsService Service +} + +func NewServer(service Service) *server { + return &server{ + factsService: service, + } +} +``` +Notice the type `coolfact.Fact`. A common anti-pattern in Go is to repeat a word in a type name. For example `http.Handler` and not `http.HttpHandler`. +In case of entities we sometimes encounter such repetition. + +`HandleGetFacts` method: + +First, we receive the slice of facts from the service and then format them to a JSON which will be returned to the client. +```go +func (s *server) HandleGetFacts(w http.ResponseWriter) { + facts, err := s.factsService.GetFacts() + if err != nil { + s.HandleError(w, fmt.Errorf("server.GetFactsHandler: %w", err)) + } + + // we first format the facts to map[string]interface. + formattedFacts := make([]map[string]interface{}, len(facts)) + for i, coolFact := range facts { + formattedFacts[i] = map[string]interface{}{ + "image": coolFact.Image, + "description": coolFact.Description, + } + } + + response := map[string]interface{}{ + "facts": formattedFacts, + } + + // code omitted +} +``` +You may ask yourself why don’t we just return the facts to the client? +This is because we want to keep separation of concerns. The entity should know nothing about the outer layer, for example the client. + +The API response and the entity field should be considered different. We don’t want that changes in the entity will have unexpected cascading changes, +and we don’t want that a requirement to modify the response will trigger a change to the entity. + +The rest of the method is to write the status and content type header, and then write the response body to the writer: +```go +func (s *server) HandleGetFacts(w http.ResponseWriter) { + // code omitted + + // write status and content-type + // status must be written before the body + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + + // write the body. We use json encoding + if err := json.NewEncoder(w).Encode(response); err != nil { + fmt.Printf("HandleGetFacts ERROR writing response: %s", err) +} +``` + +Since we are returning a JSON, we can use the Go `json` package, which can writes a JSON encoding of the response to the writer. +The `NewEncoder` method receives `io.Writer`. We already can notice the power of Go interfaces, and especially interfaces with one method. +We can send the `http.ResponseWriter` to different packages methods, and use a different encoding, and the packages are totally agnostic to HTTP. + +# Finish! +Congratulation! You've just implemented a new API with a totally cool use-case! + +In the following exercise we will add an API for creating a fact, and a client application for calling our server. \ No newline at end of file diff --git a/coolfacts/docs/ex4-initial-client.md b/coolfacts/docs/ex4-initial-client.md new file mode 100644 index 0000000..49d0112 --- /dev/null +++ b/coolfacts/docs/ex4-initial-client.md @@ -0,0 +1,161 @@ +# Part 4 + +In this exercise, you will implement a client that we will use for calling our server. +The client will sit in another main package in our `cmd` folder, making our coolfacts Go program to have 2 application. One server and one client. + +The starting point +To get started, run this command to clone the necessary exercise materials in a convenient folder: +```commandline +$ git clone --branch v4-initial-client https://github.com/FTBpro/go-workshop.git +``` + +# The Goal +After completing the exercise, you will have a client application which will take one command from the terminal, to call the server and get all the facts. + +# **Getting Started** +Take a look around the program, you notice a new cmd application - `coolfacts_client`, with two files, `main.go` and `client.go` + +## Step 0 - Notice `main.go` +In this file you don't hva any TODO, but let's get over it so you will be familiar with what that's going on + +First, we initialize the client with our server endpoint: +```go +cl := NewClient(serverEndpoint) +``` + +Then we're waiting for an input from the client: +```go + for { + fmt.Print("> ") + input, err := reader.ReadString('\n') + // code omitted + + res, err := processCmd(cl, cmd, args) + // code omitted + } +} + +func processCmd(cl *client, cmd string, args []string) (string, error) { + switch cmd { + case "": + return "", nil + case commandGetAllFacts: + // code omitted + default: + return "", errors.New("unknown command") + } +} +``` + +Currently, there is only one command `commandGetAllFacts` which is a const for "getAllFacts". + +## Step 1 - client.go - implement the client +Take a look in the file and notice we have client struct and initializer. When the user will send an input `getAllFacts`, +the client's method `GetAllFacts` will be called, and the client will call the server getFacts API. You will implement the method `GetAllFacts` + +- Fill struct `getFactsResponse` + - This struct represents the JSON response from the client, and we will use it for deserializing the server's response-body into a convenient struct. Add fields corresponding to the response. Reminder, the response looks like this: + ```json + { + "facts": [ + { + "image": "...", + "description": "..." + } + //... + ] + } + ``` + - Example on JSON tags and some json package functionality - For this be sure to add json tags on the struct. (https://gobyexample.com/json) +- Implement `ToCoolFacts()` method. You will use this method for converting the server response to the entity. +- Implement `GetAllFacts`. + - Notice the start of the method, we are composing the url for get facts which is "127.0.0.1:9002/facts" and calling `http.Get(...)`. The clinet must read and close the response after using it, you can see it in the `defer` block. + - Finish implementing the method, handle the response as specified in the TODO in the code +- Implement `readResponseGetFacts` as specified in the TODO in the code. + +## Building and Running + +If everything is implemented well, this is what the final result should look like when running the program, in one tab we are running the server as we did in the previous exercise, and in another tab we are running the client, and writing the command for getting the facts: + +TODO:(oren) add gif + +## Full Walkthrough + +In the following section you fill find a full walkthrough. Use it in case you are stuck. + +## Step 1 - client.go + +We will add fields to the struct `getFactsResponse`: +```go +type getFactsResponse struct { + Facts []struct { + Image string `json:"image"` + Description string `json:"description"` + } `json:"facts"` +} +``` +As been said, this type represent the server JSON response. The json decoding, when receiving a struct, search for the json tags, to know how to format the fields. +Note that the field are exported (public), this is for the json package to be able to see them. + +We also implement method `toCoolFacts()`. We will use this method to convert the response to the entity the client needs to return. +```go +func (r getFactsResponse) toCoolFacts() []coolfact.Fact { + coolfacts := make([]coolfact.Fact, len(r.Facts)) + for i, fact := range r.Facts { + coolfacts[i] = coolfact.Fact(fact) + } + + return coolfacts +} +``` + +Finish implementing the method `GetAllFacts` - handling the response. In case the `StatusCode` is not `http.StatusOK`, we will read an error from the response and return it. Otherwise, we read the body and convert to our coolfact.Fact slice +```go +func (c *client) GetAllFacts() ([]coolfact.Fact, error) { + + // code emitted + + if res.StatusCode != http.StatusOK { + errMessage, err := c.readError(res) + if err != nil { + return nil, fmt.Errorf("client.CreateFact: %s", err) + } + + return nil, fmt.Errorf("client.GetLastCreatedFact got an error from server. status: %d. error: %s", res.StatusCode, errMessage) + } + + getFactsRes, err := c.readResponseGetFacts(res) + if err != nil { + return nil, fmt.Errorf("client.GetLastCreatedFact: %s", err) + } + + return getFactsRes.toCoolFacts(), nil +} +``` +Notice the method `readError` which is already implemented: +```go +func (c *client) readError(res *http.Response) (string, error) { + var errRes errorResponse + if err := json.NewDecoder(res.Body).Decode(&errRes); err != nil { + return "", fmt.Errorf("readBody failed to read response body: %v. \nbody string is: %s", err) + } + + return errRes.Error, nil +} +``` +We use json package to decode the response. We will do it in the same way in the method `readResponseGetFacts`: +```go +func (c *client) readResponseGetFacts(res *http.Response) (getFactsResponse, error) { + var factsRes getFactsResponse + if err := json.NewDecoder(res.Body).Decode(&factsRes); err != nil { + return getFactsResponse{}, fmt.Errorf("readResponseGetFacts failed to read response body: %v. \nbody string is: %s", err) + } + + return factsRes, nil +} +``` + +# Finish! +Congratulation! You've just implemented a client application, and when running alongside our server, you have request-response system! + +In the following exercise we will add an API for creating a fact! diff --git a/coolfacts/docs/ex5-create-fact.md b/coolfacts/docs/ex5-create-fact.md new file mode 100644 index 0000000..31d7b12 --- /dev/null +++ b/coolfacts/docs/ex5-create-fact.md @@ -0,0 +1,359 @@ +# Part 5 + +In this exercise, you will implement a new API in the server for creating a fact. +You will also implement in the client the method that calls this API with an input from the user. +Another command that the client will export is getting the last created fact. + +The starting point +To get started, run this command to clone the necessary exercise materials in a convenient folder: +```commandline +$ git clone --branch v5-create-fact https://github.com/FTBpro/go-workshop.git +``` + +# **Getting Started** +Take a look around the program, there are a bunch of new TODOs and functionality. We will go over them in the next section. + +## Step 0 - Notice `coolfacts_clinet/main.go` +We added two new commands: +- `"createFact"` for creating a new fact in the server. +- `"getLastFact` for returning the last created fact. + In the `client`, you will implement the methods for supporting these command. + +In addition, for returning the last created fact, we need a way to which fact was created last. For supporting this, you will add a new field in the entity `coolfact.Fact` - `CreatedAt`. +For this, you will get to know the go package `time` - This package Package time provides functionality for measuring and displaying time. +Everytime we want to use field that representing a time, we will use types from this package, instead of string or int or other types. (Not include of course the http response payload). +The basic type is `time.Time` which represents an instant in time with nanosecond precision. + +For sorting, you will learn and use the go package `sort`. + +## Step 0.1 - Notice `coolfact/service_test.go` + +You can notice that wev'e added tests for our service, before you start to implement, let's understand what's in it. + +Tests files in go have the suffix `_test`. These files are not been built when you build the application. They are only considered when running the test go command: +```commandline +go test [build/test flags] [packages] [build/test flags & test binary flags] +``` + +For runnning all the tests, you need to be on the root folder (the one with the go.mod) and run +```commandline +.../coolfacts$ go test ./... +``` +Let's take a look in the file itself. notice it's package name `coolfact_test`. In Go, the only valid case for a folder to contain two packages is a test package. The suffix `_test` to the package isn't mandatory, but it helps when you only wish to test the public interface of the package. This is best for checking the interface from a real consumer POV. + +When running the `go test` command, Go searchs in all the `_test.go` files for functions with `Test` prefix. These files takes one argument `t *testing.T` which is a type passed to Test functions to manage test state and support formatted test logs. + +Test functions can be named anything with a `Test` prefix, but there is some convention: +```go +func Test__ +``` +In here, we test our `service` methods `GetFacts` and `CreateFacts`, so our test functions are named +```go +func Test_service_AllFacts(t *testing.T) {...} + +func Test_service_CreateFact(t *testing.T) {...} +``` + +The structure of the test is an example of _Table Driven Tests_: + +```go +func Test...(t *testing.T) { + type testCase struct {...} + + tests := []testCase{ + {...}, + {...}, + } + + for _, tc := range tests { + t.Run(tc.Name, func(t *testing.T) { + // Here we write the test itself + }) + } +} +``` +We declaring type `testCase` which is a struct that holds the parameters for the tests, these can be: +- Name of the test. +- Input for initializing the service. +- Arguments for the methods. +- Expected result. +- Indicator if we expect an error. + +In the `t.Run` method we write the test. In `GetFacts` for example, we initialize the service with the repo we set in the `repoField` field, call the method `GetFacts` and expected to receive either en error or what we set in the field `want`. + +You can also notice, that in the first test we've used `t.Fatal`, and in the second test we've used `require`. `require` is a package that provides helpful methods for testing. It also prints the failure in a more readable way. Since it's an external library, you can see that we've added a require in `go.mod`. + +## Step 1 - Implement The BL +### coolfact/fact.go +- For supporting the sorting, add the field for the createdAt. + +### coolfact/service.go +The service has new functionality for creating a fact. +- In the `Repository` interface, add the method that the service requires. +- Implement the method `CreateFact`. + +## Step 2 - The repo +The repository now have new method for creating a fact. In addition you need to change the method for getting facts and sort them by their created time. +- In `GetFacts` add the sorting. For the sort, you will use the type `byCreatedAt`. To learn about sorting using `sort.Sort`, read the TODO in the code in the `GetFacts` method. +- Implement the method `CreateFact` + +## Step 3 - Server +The service has a new API: +```json +POST "/facts" + +Request: +{ +"image": "...", +"description": "..." +} + +Response: +Success: 200 +Failure: +- Missing field: 404 +- Error in the server: 500 +``` +- The returned facts from the service should have a new field for the created time. Add this field in the formatted response in the method `HandleGetFacts` +- Add the required method in the `FactsService` interface. +- Add a new case in the method `ServeHTTP`. Note that the http method is POST request, and the path is "/path". In case of a such a request, call the server method `HandleCreateFact` + - Add struct `factService` for decoding the request into. This struct should be the representation of the request body. +- Implement method `HandleCreateFact`. + +## Step 4 - The Client +- Implement method `GetLastCreatedFact`. Note that the server doesn't have a dedicated API for this, so use the method `GetAllFacts` + - Of course in real world application it's not a good practice, but this exercise is not a real world application. + - Since the server returns a new field for the created time, add this field in the struct `getFactsResponse` which is the representation of the response. +- Implement the method `CreateFact`. + +## Building and Running + +If everything is implemented well, this is what the final result should look like when running the program, in one tab we are running the server as we did in the previous exercise, and in another tab we are running the client, and writing the command for getting the facts: + +TODO:(oren) add gif + +## Full Walkthrough + +## Step 1 - Implement The BL +### coolfact/fact.go +In our entity, we add a new field `CreatedAt` for specifying the time the fact was created. +```go +type Fact struct { + Image string + Description string + CreatedAt time.Time // new +} +``` + +### coolfact/service.go +The service requires another method from the `Repository` interface +```go +type Repository interface { + GetFacts() ([]Fact, error) + CreateFact(fct Fact) error // new +} +``` +And in the `CreateFact` we just calling the repo: +```go +func (s *service) CreateFact(fact Fact) error { + if err := s.factsRepo.CreateFact(fact); err != nil { + return fmt.Errorf("factsService.CreateFact: %w", err) + } + + return nil +} +``` + +## Step 2 - The repo +In the `CreateFacts`, before reurning the facts, we will sort them based on their `CreatedAt` +```go +func (r *factsRepo) GetFacts() ([]coolfact.Fact, error) { + sort.Sort(byCreatedAt(r.facts)) + + return r.facts, nil +} +``` +We use method `sort.Sort`. This method expects an argument that implements `sort.Interface`: +```go +type Interface interface { + Len() int + Less(i, j int) bool + Swap(i, j int) +} +``` +Using these methods, it sorts the given slice. Since we can't our `r.facts` slice (because it doesn't implement the `sort.Interface`), we will use the type `byCreatedAt`: +```go +type byCreatedAt []coolfact.Fact + +func (s byCreatedAt) Len() int { + return len(s) +} + +func (s byCreatedAt) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s byCreatedAt) Less(i, j int) bool { + return s[i].CreatedAt.After(s[j].CreatedAt) +} +``` + +Note the type conversion: `byCreatedAt(r.facts)`. Type conversion is simply to convert some value to other type. +This is possible since the two types are compatible. + +## Step 3 - Server +The service exports a new API for creating the fact. The http method is `POST`, and the path is `"/facts"`. We will add a new case in our `ServeHTTP` method: +```go +func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + log.Println("incoming request", r.Method, r.URL.Path) + + switch r.Method { + case http.MethodGet: + // code omitted + case http.MethodPost: + switch strings.ToLower(r.URL.Path) { + case "/facts": + s.HandleCreateFact(w, r) + default: + err := fmt.Errorf("path %q wasn't found", r.URL.Path) + s.HandleNotFound(w, err) + } + default: + // code omitted + } +} +``` +For decoding the request, we would use a struct `createFactRequest` which is a representation of the request payload: +```go +type createFactRequest struct { + Image string `json:"image"` + Description string `json:"description"` +} + +func (r createFactRequest) ToCoolFact() coolfact.Fact { + return coolfact.Fact{ + Image: r.Image, + Description: r.Description, + CreatedAt: time.Now(), + } +} +``` +Note that in the method `ToCoolFact` we also set the `CreatedAt` to `time.Now()`. + +And then we could implement `HandleCreateFact`: +```go +func (s *server) HandleCreateFact(w http.ResponseWriter, r *http.Request) { + log.Println("Handling createFact ...") + + var request createFactRequest + if err := json.NewDecoder(r.Body).Decode(&request); err != nil { + err = fmt.Errorf("server.HandleCreateFact failed to decode request: %s", err) + s.HandleError(w, err) + return + } + + if err := s.factsService.CreateFact(request.ToCoolFact()); err != nil { + err = fmt.Errorf("server.HandleCreateFact: %s", err) + s.HandleError(w, err) + return + } + + w.WriteHeader(http.StatusOK) +} +``` + +Finally, in `HandleGetFacts` we just need to add the `createdAt` to the response: +```go +func (s *server) HandleGetFacts(w http.ResponseWriter) { + log.Println("Handling getFact ...") + facts, err := s.factsService.GetFacts() + if err != nil { + formattedFacts[i] = map[string]interface{}{ + "image": coolFact.Image, + "description": coolFact.Description, + "createdAt": coolFact.CreatedAt, // new + } + } + + // code omitted +} + +``` + +## Step 4 - The Client +The client receives the response of the facts from the service, so we'll add the field `createdAt`, so it will be decoded as well: +```go +type getFactsResponse struct { + Facts []struct { + Image string `json:"image"` + Description string `json:"description"` + CreatedAt time.Time `json:"createdAt"` // new + } `json:"facts"` +} +``` + +We will implement the method GetLAstFact using the existing method `GetAllFacts`: +```go +func (c *client) GetLastCreatedFact() (coolfact.Fact, error) { + allFacts, err := c.GetAllFacts() + if err != nil { + return coolfact.Fact{}, fmt.Errorf("GetLastCreatedFact: %w", err) + } + + if len(allFacts) == 0 { + return coolfact.Fact{}, fmt.Errorf("fact not found") + } + + return allFacts[0], nil +} + +``` + +And finally, we implement the method `CreateFact` which call the service +```go +func (c *client) CreateFact(fct coolfact.Fact) error { + ul := c.endpoint + pathCreateFact + // First we are preparing the payload + payload := map[string]interface{}{ + "image": fct.Image, + "description": fct.Description, + } + // we need io.Reader to create a new http request. + // we will create bytes.Buffer which implement this interface + postBody, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("client.CreateFact failed to marshal payload: %v", err) + } + responseBody := bytes.NewBuffer(postBody) + + req, err := http.NewRequest(http.MethodPost, ul, responseBody) + if err != nil { + return fmt.Errorf("client.CreateFact failed to create request: %v", err) + } + + res, err := c.httpClient.Do(req) + if err != nil { + return fmt.Errorf("client.CreateFact failed to do request: %v", err) + } + + defer func() { + if res != nil && res.Body != nil { + _, _ = io.Copy(ioutil.Discard, res.Body) + _ = res.Body.Close() + } + }() + + if res.StatusCode != http.StatusOK { + errMessage, err := c.readError(res) + if err != nil { + return fmt.Errorf("client.CreateFact: %s", err) + } + + return fmt.Errorf("client.CreateFact got an error from server. status: %d. error: %s", res.StatusCode, errMessage) + } + + return nil +} +``` + +# Finish! \ No newline at end of file diff --git a/coolfacts/docs/ex6-search-facts.md b/coolfacts/docs/ex6-search-facts.md new file mode 100644 index 0000000..45ab4d1 --- /dev/null +++ b/coolfacts/docs/ex6-search-facts.md @@ -0,0 +1,172 @@ +# Part 6 + +In this exercise you will add some more interesting use case for the get facts API. The option to search facts by topic, and add a limit. + +### The starting point +To get started, run this command to clone the necessary exercise materials in a convenient folder: +```commandline +$ git clone --branch v5-create-fact https://github.com/FTBpro/go-workshop.git +``` + +# **Getting Started** +You will add two query params to the get-facts API. `limit` and `topic`. Usually, we don't want that the client will get all resources, so we require they to send a limit. We also add the option to filter facts by a specific topic. + +In the client, we've added args to the command `getFacts` +```commandline +$ > getFacts [limit] [topic (optional)] +``` +You will implement this new ability at the server side. + +The tests were updated for testing the new functionality. After all is implemented they should pass. + +## Step 1 - coolfact/fact.go + +For supporting the new use case, you will add new type in the entity package coolfact. The function `GetFacts` will be changed to `SearchFacts` since it will get the filters and the repo will need to search the appropriate facts instead of just returns all of them. + +- Add the type `Filters` that will hold the fields that can be used for filtering the facts. + +## Step 2 - fix signatures +As said, the method `GetFacts` should be called `SearchFacts` and should receive arg `coolfact.Filters` +- Go over the project and fix all the signatures of the service and repo methods and in all the interfaces that requires them. There is a TODO next to any method/interface that needs to be fixed. + +## Step 3 - inmem/factsrepo.go +- In here you can notice that the type `factsRepo` now holds `factsByTopic map[string][]coolfact.Fact` instead of just `facts []coolfact.Fact`. Use this field and fix all the implementation inside the repo. + - `NewFactsRepository` - fix implementation. + - `GetFacts` - fix method name/signature and implementation. + - Note that `topic` is optional. + - `CreateFact` - fix implementation. + +## Step 3 - coolfact_service/server.go +- In `HandleGetFacts`, reaad the query params from the request and use them for inialize `coolfact.Filters` struct to be sent to the service method. + - Use `r.URL.Query()` for accessing the query params. + - Note that the limit is mandatory, meaning that if there isn't limit (or the limit isn't string), you should return bad request. In this case call the method `HandleBadRequest` which you will implement. +- Implement `HandleBadRequest`. Just like any other error response, but the status should be 400 (`http.StatusBadRequest`) + +# Build And Run +If all is implemented, you should be able to run tests and see them pass. +And you should see this: + +TODO: add gif + +# full Walkthrough + +## Step 1 - coolfact/fact.go +We'll add the new type for the available filters: +```go +type Filters struct { + Topic string + Limit int +} +``` + +## Step 2 -fix signature + +The method `GetFacts` is changed to `SearchFacts(filters coolfact.Filters) ([]coolfact.Fact, error)` + +We'll change in the service: +```go +type Repository interface { + SearchFacts(filters Filters) ([]Fact, error) // Was changed from `GetFacts` + CreateFact(fct Fact) error +``` +```go +func (s *service) SearchFacts(filters Filters) ([]Fact, error) {...} // Was changed from `GetFacts` +``` + +In the repo: +```go +func (r *factsRepo) SearchFacts(filters coolfact.Filters) ([]coolfact.Fact, error) {...} +``` + +And in the server: +```go +type FactsService interface { + SearchFacts(filters coolfact.Filters) ([]coolfact.Fact, error) // Was changed from `GetFacts` + CreateFact(fact coolfact.Fact) error +} +``` + +## Step 3 - the repo +We'll fix the initializer. We'll go over the facts, and add them under the right key +```go +func NewFactsRepository(facts ...coolfact.Fact) *factsRepo { + factsByTopic := map[string][]coolfact.Fact{} + for _, fact := range facts { + factsByTopic[fact.Topic] = append(factsByTopic[fact.Topic], fact) + } + + return &factsRepo{ + factsByTopic: factsByTopic, + } +} +``` +We'll fix the `GetFacts` to support the new filters: (Note that it is only one example of implementation, not focusing on performance at all) +```go +func (r *factsRepo) SearchFacts(filters coolfact.Filters) ([]coolfact.Fact, error) { + var facts []coolfact.Fact + if filters.Topic != "" { + facts = r.factsByTopic[filters.Topic] + } else { + facts = r.allFacts() + } + + sort.Sort(byCreatedAt(facts)) + + if filters.Limit < len(facts) { + facts = facts[:filters.Limit] + } + + return facts, nil +} + +func (s *factsRepo) allFacts() []coolfact.Fact { + var allFacts []coolfact.Fact + for _, facts := range s.factsByTopic { + allFacts = append(allFacts, facts...) + } + + return allFacts +} +``` + +Finally, we'll fix the `CreateFact` method, just add the fact to the facts of its topic: +```go +func (r *factsRepo) CreateFact(fact coolfact.Fact) error { + r.factsByTopic[fact.Topic] = append(r.factsByTopic[fact.Topic], fact) + + return nil +} +``` + +## Step 4 - Server +In `HandleGetFacts`, the server needs to call the service's method `SearchFact(filters)`. The filters will be built from the query params of the request. For example, if the client wish to get 10 facts of topic `TV`, it will call: +```commandline +GET /facts?limit=10&topic=TV +``` +Both the arguments will be received as strings, but we will need to convert the limit top int. If we can't, we'll return bad request +```go +func (s *server) HandleGetFacts(w http.ResponseWriter, r *http.Request) { + log.Println("Handling getFact ...") + + limitString := r.URL.Query().Get("limit") + if limitString == "" || limitString == "0" { + err := fmt.Errorf("limit is mandatory") + s.HandleBadRequest(w, err) + } + + limit, err := strconv.Atoi(limitString) + if err != nil { + err = fmt.Errorf("limit isn't int") + s.HandleBadRequest(w, err) + } + + filters := coolfact.Filters{ + Topic: r.URL.Query().Get("topic"), + Limit: limit, + } + + facts, err := s.factsService.GetFacts() + // code omitted +``` + +# Finish :boom: \ No newline at end of file diff --git a/coolfacts/docs/ex7-basic-router.md b/coolfacts/docs/ex7-basic-router.md new file mode 100644 index 0000000..44899a7 --- /dev/null +++ b/coolfacts/docs/ex7-basic-router.md @@ -0,0 +1,70 @@ +# Part 7 + +In this exercise you will build a simple router for acting as the main `http.Handler` and to direct the request into the appropriate handler + +### The starting point +To get started, run this command to clone the necessary exercise materials in a convenient folder: +```commandline +$ git clone --branch v5-create-fact https://github.com/FTBpro/go-workshop.git +``` + +# **Getting Started** +Until the last exercise, our server acted as the `http.Handler` to the method `http.ListenAndServe`. The method `server.ServeHTTP` handled the logic for directing the request into the right handler. For every new route you'll had to add a case in this method. + +In real-world application we usually would prefer to use a framework over http that exports a router for more convinient handling. +- Automatically matches a Method + Path to an `http.Handler`. +- Easy path params handling. For example `/facts/:factID` -> the `factID` param will be given to us by the router. +- Usually we will have more than one domain in the application, for each domain we will have its specific routes. Using a shared router will make it easier. +- Easy to add middlewares across all the APIs. + +In this part we will implement a basic router that export a method for registering to a route: +```go +func Handle(method, path string, handler http.HandlerFunc) +``` + +Our server will use it for each route, instead of handling itself the logic inside the `ServeHTTP` method. Meaning, our server won't be an `http.Handler` anymore. + +going over the branch, you will notice a couple of changes, and a new package - `coolhttp`. In this package we will implement our router. This package is agnostic to the application, meaning doesn't know anything about the server or the coolfacts. In a system of many services, this package could be shared between all of them. + +## Step 0 - Notice `coolfacts_server/main.go` +We are initializing our router and setting its not found handler: +```go + router := coolhttp.NewRouter() + router.SetNotFoundHandler(server.HandleNotFound) +``` +Then we are calling a new method in the server: +```go +server.RegisterRouter(router) +``` +This server uses the router for registering any route with the corresponding handler. Later you'll implement it. + +Finally, we use this router: +```go + if err := http.ListenAndServe(":9002", router); err != nil { + //... + } +``` + +Every request will get to the `router.ServeHTTP` which you will implement. + +## Step 1 - Implement the router (in coolhttp/router.go) +Heading over to the coolhttp/router.go file, you'll see the new package. You'll need to implement its methods: +```go +func (r *router) Handle(method, path string, handler http.HandlerFunc) {...} + +func (r *router) ServeHTTP(w http.ResponseWriter, req *http.Request) {...} +``` +The `Handle` method will be called for each route, on registration from the server. You will need to same some sort of state for handling the logic, so that in the method `ServeHTTP` you could call to the right `http.HandlerFunc` that was registered to the corresponding http method and path. + +If no such handler exist, you should call the `notFoundHandler`. + +## Step 2 - coolfacts_server/server.go +You will see the the method `ServeHTTP` was deleted, and instead there is a new method `func (s *server) RegisterRouter(router Router)`. +Implement this method. Reminder, the routes are: +- GET "/ping" +- GET "/facts" +- POST "/facts" + +# Test, Build and Run + +## Full Walkthrough \ No newline at end of file From d8e4f0aeba98450faa65f7c33d720f8472ff329b Mon Sep 17 00:00:00 2001 From: Oren Rosen Date: Tue, 6 Dec 2022 14:29:12 +0200 Subject: [PATCH 07/10] delete dep --- coolfacts-dep/exercise1/go.mod | 3 - coolfacts-dep/exercise1/main.go | 28 ---- coolfacts-dep/exercise2/fact.go | 19 --- coolfacts-dep/exercise2/go.mod | 3 - coolfacts-dep/exercise2/main.go | 63 -------- coolfacts-dep/exercise3/fact.go | 19 --- coolfacts-dep/exercise3/go.mod | 3 - coolfacts-dep/exercise3/main.go | 100 ------------ coolfacts-dep/exercise4/fact.go | 19 --- coolfacts-dep/exercise4/go.mod | 3 - coolfacts-dep/exercise4/main.go | 139 ----------------- coolfacts-dep/exercise5/fact.go | 19 --- coolfacts-dep/exercise5/go.mod | 3 - coolfacts-dep/exercise5/main.go | 139 ----------------- coolfacts-dep/exercise5/mentalfloss.go | 57 ------- coolfacts-dep/exercise6/fact/fact.go | 19 --- coolfacts-dep/exercise6/fact/fact_test.go | 25 --- coolfacts-dep/exercise6/go.mod | 3 - coolfacts-dep/exercise6/http/handler.go | 130 ---------------- coolfacts-dep/exercise6/main.go | 40 ----- .../exercise6/mentalfloss/mentalfloss.go | 57 ------- coolfacts-dep/exercise7/fact/fact.go | 19 --- coolfacts-dep/exercise7/go.mod | 3 - coolfacts-dep/exercise7/go.sum | 0 coolfacts-dep/exercise7/http/handler.go | 130 ---------------- coolfacts-dep/exercise7/main.go | 73 --------- .../exercise7/mentalfloss/mentalfloss.go | 57 ------- coolfacts-dep/exercise8/fact/fact.go | 6 - coolfacts-dep/exercise8/fact/service.go | 39 ----- coolfacts-dep/exercise8/go.mod | 3 - coolfacts-dep/exercise8/go.sum | 2 - coolfacts-dep/exercise8/http/handler.go | 142 ------------------ coolfacts-dep/exercise8/inmem/factstore.go | 22 --- coolfacts-dep/exercise8/main.go | 59 -------- coolfacts-dep/exercise8/mentalfloss/item.go | 15 -- .../exercise8/mentalfloss/provider.go | 58 ------- coolfacts-dep/go.mod | 30 ---- coolfacts-dep/styles.css | 30 ---- 38 files changed, 1579 deletions(-) delete mode 100644 coolfacts-dep/exercise1/go.mod delete mode 100644 coolfacts-dep/exercise1/main.go delete mode 100644 coolfacts-dep/exercise2/fact.go delete mode 100644 coolfacts-dep/exercise2/go.mod delete mode 100644 coolfacts-dep/exercise2/main.go delete mode 100644 coolfacts-dep/exercise3/fact.go delete mode 100644 coolfacts-dep/exercise3/go.mod delete mode 100644 coolfacts-dep/exercise3/main.go delete mode 100644 coolfacts-dep/exercise4/fact.go delete mode 100644 coolfacts-dep/exercise4/go.mod delete mode 100644 coolfacts-dep/exercise4/main.go delete mode 100644 coolfacts-dep/exercise5/fact.go delete mode 100644 coolfacts-dep/exercise5/go.mod delete mode 100644 coolfacts-dep/exercise5/main.go delete mode 100644 coolfacts-dep/exercise5/mentalfloss.go delete mode 100644 coolfacts-dep/exercise6/fact/fact.go delete mode 100644 coolfacts-dep/exercise6/fact/fact_test.go delete mode 100644 coolfacts-dep/exercise6/go.mod delete mode 100644 coolfacts-dep/exercise6/http/handler.go delete mode 100644 coolfacts-dep/exercise6/main.go delete mode 100644 coolfacts-dep/exercise6/mentalfloss/mentalfloss.go delete mode 100644 coolfacts-dep/exercise7/fact/fact.go delete mode 100644 coolfacts-dep/exercise7/go.mod delete mode 100644 coolfacts-dep/exercise7/go.sum delete mode 100644 coolfacts-dep/exercise7/http/handler.go delete mode 100644 coolfacts-dep/exercise7/main.go delete mode 100644 coolfacts-dep/exercise7/mentalfloss/mentalfloss.go delete mode 100644 coolfacts-dep/exercise8/fact/fact.go delete mode 100644 coolfacts-dep/exercise8/fact/service.go delete mode 100644 coolfacts-dep/exercise8/go.mod delete mode 100644 coolfacts-dep/exercise8/go.sum delete mode 100644 coolfacts-dep/exercise8/http/handler.go delete mode 100644 coolfacts-dep/exercise8/inmem/factstore.go delete mode 100644 coolfacts-dep/exercise8/main.go delete mode 100644 coolfacts-dep/exercise8/mentalfloss/item.go delete mode 100644 coolfacts-dep/exercise8/mentalfloss/provider.go delete mode 100644 coolfacts-dep/go.mod delete mode 100644 coolfacts-dep/styles.css diff --git a/coolfacts-dep/exercise1/go.mod b/coolfacts-dep/exercise1/go.mod deleted file mode 100644 index 5527b1a..0000000 --- a/coolfacts-dep/exercise1/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise1 - -go 1.13 diff --git a/coolfacts-dep/exercise1/main.go b/coolfacts-dep/exercise1/main.go deleted file mode 100644 index abf4066..0000000 --- a/coolfacts-dep/exercise1/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" - "log" - "net/http" -) - -func main() { - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts-dep/exercise2/fact.go b/coolfacts-dep/exercise2/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts-dep/exercise2/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts-dep/exercise2/go.mod b/coolfacts-dep/exercise2/go.mod deleted file mode 100644 index 9170812..0000000 --- a/coolfacts-dep/exercise2/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise2 - -go 1.13 diff --git a/coolfacts-dep/exercise2/main.go b/coolfacts-dep/exercise2/main.go deleted file mode 100644 index c51354c..0000000 --- a/coolfacts-dep/exercise2/main.go +++ /dev/null @@ -1,63 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - "net/http" -) - -func main() { - factsRepo := repo{} - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", - Description: "Did you know sonic is a hedgehog?!", - }) - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", - Description: "You won't believe what happened to Arya!", - }) - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - // We are checking that we are answering only for GET method - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "application/json") - - // Here we are getting the JSON encoding of for the all facts from the repo - b, err := json.Marshal(factsRepo.getAll()) - if err != nil { - errMessage := fmt.Sprintf("error marshaling facts : %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - // Next we are writing it the the http.ResponseWriter - _, err = w.Write(b) - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts-dep/exercise3/fact.go b/coolfacts-dep/exercise3/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts-dep/exercise3/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts-dep/exercise3/go.mod b/coolfacts-dep/exercise3/go.mod deleted file mode 100644 index 097c981..0000000 --- a/coolfacts-dep/exercise3/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise3 - -go 1.13 diff --git a/coolfacts-dep/exercise3/main.go b/coolfacts-dep/exercise3/main.go deleted file mode 100644 index ba7abaf..0000000 --- a/coolfacts-dep/exercise3/main.go +++ /dev/null @@ -1,100 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" -) - -func main() { - factsRepo := repo{} - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", - Description: "Did you know sonic is a hedgehog?!", - }) - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", - Description: "You won't believe what happened to Arya!", - }) - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - - w.Header().Add("Content-Type", "text/plain") - - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet { - w.Header().Add("Content-Type", "application/json") - - b, err := json.Marshal(factsRepo.getAll()) - if err != nil { - errMessage := fmt.Sprintf("error marshaling facts : %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - _, err = w.Write(b) - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - } - if r.Method == http.MethodPost { - // first read the request body into a byte stream - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - // We are expecting the payload to be from the form: - // { - // "image": "image name", - // "url": "image/url", - // "description": "image description" - // } - // We will use this struct representation of the expected request body, and parse into it the data - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - - // parse the JSON-encoded data and stores the result in req - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - return - } - - // create a new fact from this request struct, and repo it using the repo - f := fact{ - Image: req.Image, - Description: req.Description, - } - - factsRepo.add(f) - w.Write([]byte("SUCCESS")) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts-dep/exercise4/fact.go b/coolfacts-dep/exercise4/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts-dep/exercise4/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts-dep/exercise4/go.mod b/coolfacts-dep/exercise4/go.mod deleted file mode 100644 index 6a8c003..0000000 --- a/coolfacts-dep/exercise4/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise4 - -go 1.13 diff --git a/coolfacts-dep/exercise4/main.go b/coolfacts-dep/exercise4/main.go deleted file mode 100644 index fdc59da..0000000 --- a/coolfacts-dep/exercise4/main.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "log" - "net/http" -) - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func main() { - factsRepo := repo{} - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556645500/shape/cover/entertainment/D5aliXvWsAEcYoK-fe997566220c082b98030508e654948e.jpg", - Description: "Did you know sonic is a hedgehog?!", - }) - factsRepo.add(fact{ - Image: "https://images2.minutemediacdn.com/image/upload/v1556641470/shape/cover/entertainment/uncropped-Screen-Shot-2019-04-30-at-122411-PM-3b804f143c543dfab4b75c81833bed1b.jpg", - Description: "You won't believe what happened to Arya!", - }) - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet { - w.Header().Add("Content-Type", "text/html") - - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - facts := factsRepo.getAll() - err = tmpl.Execute(w, facts) - if err != nil { - errMessage := fmt.Sprintf("failed parse template: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - return - } - if r.Method == http.MethodPost { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - - f := fact{ - Image: req.Image, - Description: req.Description, - } - - factsRepo.add(f) - w.Write([]byte("SUCCESS")) - } - }) - - log.Println("starting server") - err := http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts-dep/exercise5/fact.go b/coolfacts-dep/exercise5/fact.go deleted file mode 100644 index 5023d28..0000000 --- a/coolfacts-dep/exercise5/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -type fact struct { - Image string - Description string -} - -type repo struct { - facts []fact -} - -func (s *repo) add(f fact) { - // append is a Go built in function - s.facts = append(s.facts, f) -} - -func (s *repo) getAll() []fact { - return s.facts -} diff --git a/coolfacts-dep/exercise5/go.mod b/coolfacts-dep/exercise5/go.mod deleted file mode 100644 index ada5c65..0000000 --- a/coolfacts-dep/exercise5/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise5 - -go 1.13 diff --git a/coolfacts-dep/exercise5/main.go b/coolfacts-dep/exercise5/main.go deleted file mode 100644 index 1ca150a..0000000 --- a/coolfacts-dep/exercise5/main.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "log" - "net/http" -) - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func main() { - factsRepo := repo{} - mentalflossProvider := mentalfloss{} - facts, err := mentalflossProvider.Facts() - if err != nil { - log.Fatal("can't reach mentalfloss: ", err) - } - for _, f := range facts { - factsRepo.add(f) - } - - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } - }) - - http.HandleFunc("/facts", func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet { - w.Header().Add("Content-Type", "text/html") - - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - facts := factsRepo.getAll() - err = tmpl.Execute(w, facts) - if err != nil { - errMessage := fmt.Sprintf("failed parse template: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - - return - } - if r.Method == http.MethodPost { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - - f := fact{ - Image: req.Image, - Description: req.Description, - } - - factsRepo.add(f) - w.Write([]byte("SUCCESS")) - } - }) - - log.Println("starting server") - err = http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} diff --git a/coolfacts-dep/exercise5/mentalfloss.go b/coolfacts-dep/exercise5/mentalfloss.go deleted file mode 100644 index a62a622..0000000 --- a/coolfacts-dep/exercise5/mentalfloss.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" -) - -type mentalfloss struct{} - -func (mf mentalfloss) Facts() ([]fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get("http://mentalfloss.com/api/facts") - if err != nil { - return nil, fmt.Errorf("error making request: %v", err) - } - log.Println("got facts from mentalfloss successfully") - - // A `defer` statement defers the execution of a function until the surrounding function returns. - // This is how we make sure that we close the response body before we exit the function. - defer resp.Body.Close() - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll: %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data: %v", err) - } - - return facts, nil -} - -func parseFromRawItems(b []byte) ([]fact, error) { - var items []struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` - } - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - var facts []fact - for _, it := range items { - newFact := fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts-dep/exercise6/fact/fact.go b/coolfacts-dep/exercise6/fact/fact.go deleted file mode 100644 index 2e15ecc..0000000 --- a/coolfacts-dep/exercise6/fact/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package fact - -type Fact struct { - Image string - Description string -} - -type Repository struct { - facts []Fact -} - -func (r *Repository) Add(f Fact) { - // append is a Go built in function - r.facts = append(r.facts, f) -} - -func (r *Repository) GetAll() []Fact { - return r.facts -} diff --git a/coolfacts-dep/exercise6/fact/fact_test.go b/coolfacts-dep/exercise6/fact/fact_test.go deleted file mode 100644 index 7d76ecc..0000000 --- a/coolfacts-dep/exercise6/fact/fact_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package fact - -import ( - "testing" -) - -func TestRepo_Add(t *testing.T) { - repo := Repository{} - fact := Fact{ - Image: "img", - Description: "desc", - } - repo.Add(fact) - - gotFacts := repo.GetAll() - var hasItem bool - for _, gotFact := range gotFacts { - if gotFact.Image == fact.Image && gotFact.Description == fact.Description { - hasItem = true - } - } - if !hasItem { - t.Errorf("can't find added fact") - } -} diff --git a/coolfacts-dep/exercise6/go.mod b/coolfacts-dep/exercise6/go.mod deleted file mode 100644 index 2cbafe9..0000000 --- a/coolfacts-dep/exercise6/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise6 - -go 1.13 diff --git a/coolfacts-dep/exercise6/http/handler.go b/coolfacts-dep/exercise6/http/handler.go deleted file mode 100644 index dfd8598..0000000 --- a/coolfacts-dep/exercise6/http/handler.go +++ /dev/null @@ -1,130 +0,0 @@ -package http - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" -) - -type FactsHandler struct { - FactRepo *fact.Repository -} - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func (h *FactsHandler) Ping(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } -} - -func (h *FactsHandler) Facts(w http.ResponseWriter, r *http.Request) { - if h.FactRepo == nil { - http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) - } - - switch r.Method { - case http.MethodGet: - h.showFacts(w) - return - case http.MethodPost: - h.postFacts(r, w) - return - default: - http.Error(w, "no http handler found", http.StatusNotFound) - } -} - -func (h *FactsHandler) postFacts(r *http.Request, w http.ResponseWriter) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - f := fact.Fact{ - Image: req.Image, - Description: req.Description, - } - h.FactRepo.Add(f) - w.Write([]byte("SUCCESS")) -} - -func (h *FactsHandler) showFacts(w http.ResponseWriter) { - w.Header().Add("Content-Type", "text/html") - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - tmpl.Execute(w, h.FactRepo.GetAll()) -} diff --git a/coolfacts-dep/exercise6/main.go b/coolfacts-dep/exercise6/main.go deleted file mode 100644 index dd8c193..0000000 --- a/coolfacts-dep/exercise6/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" - facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise6/http" - "github.com/FTBpro/go-workshop/coolfacts/exercise6/mentalfloss" -) - -func main() { - factsRepo := fact.Repository{} - handlerer := facthttp.FactsHandler{ - FactRepo: &factsRepo, - } - - mentalflossProvider := mentalfloss.Mentalfloss{} - err := updateFacts(mentalflossProvider, &factsRepo) - - http.HandleFunc("/ping", handlerer.Ping) - http.HandleFunc("/facts", handlerer.Facts) - - log.Println("starting server") - err = http.ListenAndServe(":9002", nil) - if err != nil { - log.Fatal(err) - } -} - -func updateFacts(mf mentalfloss.Mentalfloss, factsRepo *fact.Repository) error { - facts, err := mf.Facts() - if err != nil { - log.Fatal("can't reach mentalfloss: ", err) - } - for _, f := range facts { - factsRepo.Add(f) - } - return err -} diff --git a/coolfacts-dep/exercise6/mentalfloss/mentalfloss.go b/coolfacts-dep/exercise6/mentalfloss/mentalfloss.go deleted file mode 100644 index 04923d3..0000000 --- a/coolfacts-dep/exercise6/mentalfloss/mentalfloss.go +++ /dev/null @@ -1,57 +0,0 @@ -package mentalfloss - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise6/fact" -) - -type Mentalfloss struct { -} - -func (mf Mentalfloss) Facts() ([]fact.Fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get("http://mentalfloss.com/api/facts") - if err != nil { - return nil, fmt.Errorf("error making request: %v", err) - } - defer resp.Body.Close() - log.Println("got facts from mentalfloss successfully") - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll: %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data: %v", err) - } - - return facts, nil -} - -func parseFromRawItems(b []byte) ([]fact.Fact, error) { - var items []struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` - } - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - var facts []fact.Fact - for _, it := range items { - newFact := fact.Fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts-dep/exercise7/fact/fact.go b/coolfacts-dep/exercise7/fact/fact.go deleted file mode 100644 index 0b3485d..0000000 --- a/coolfacts-dep/exercise7/fact/fact.go +++ /dev/null @@ -1,19 +0,0 @@ -package fact - -type Fact struct { - Image string - Description string -} - -type Repository struct { - facts []Fact -} - -func (r *Repository) Add(f Fact) { - // append is a Go built in function - r.facts = append([]Fact{f}, r.facts...) -} - -func (r *Repository) GetAll() []Fact { - return r.facts -} diff --git a/coolfacts-dep/exercise7/go.mod b/coolfacts-dep/exercise7/go.mod deleted file mode 100644 index 75ed072..0000000 --- a/coolfacts-dep/exercise7/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise7 - -go 1.12 diff --git a/coolfacts-dep/exercise7/go.sum b/coolfacts-dep/exercise7/go.sum deleted file mode 100644 index e69de29..0000000 diff --git a/coolfacts-dep/exercise7/http/handler.go b/coolfacts-dep/exercise7/http/handler.go deleted file mode 100644 index af02f38..0000000 --- a/coolfacts-dep/exercise7/http/handler.go +++ /dev/null @@ -1,130 +0,0 @@ -package http - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" -) - -type FactsHandler struct { - FactRepo *fact.Repository -} - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - -` - -func (h *FactsHandler) Ping(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } -} - -func (h *FactsHandler) Facts(w http.ResponseWriter, r *http.Request) { - if h.FactRepo == nil { - http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) - } - - switch r.Method { - case http.MethodGet: - h.showFacts(w) - return - case http.MethodPost: - h.postFacts(r, w) - return - default: - http.Error(w, "no http handler found", http.StatusNotFound) - } -} - -func (h *FactsHandler) postFacts(r *http.Request, w http.ResponseWriter) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - f := fact.Fact{ - Image: req.Image, - Description: req.Description, - } - h.FactRepo.Add(f) - w.Write([]byte("SUCCESS")) -} - -func (h *FactsHandler) showFacts(w http.ResponseWriter) { - w.Header().Add("Content-Type", "text/html") - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - tmpl.Execute(w, h.FactRepo.GetAll()) -} diff --git a/coolfacts-dep/exercise7/main.go b/coolfacts-dep/exercise7/main.go deleted file mode 100644 index 16d6414..0000000 --- a/coolfacts-dep/exercise7/main.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "context" - "log" - "net/http" - "time" - - "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" - facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise7/http" - "github.com/FTBpro/go-workshop/coolfacts/exercise7/mentalfloss" -) - -const ( - updateFactInterval = time.Minute * 1 -) - -func main() { - factsRepo := fact.Repository{} - handlerer := facthttp.FactsHandler{ - FactRepo: &factsRepo, - } - - mentalflossProvider := mentalfloss.Mentalfloss{} - updateFunc := updateFactsFunc(mentalflossProvider, &factsRepo) - if err := updateFunc(); err != nil { - log.Fatal(err) - } - - ctx, closer := context.WithCancel(context.Background()) - defer closer() - updateFactsWithTicker(ctx, updateFunc) - - http.HandleFunc("/ping", handlerer.Ping) - http.HandleFunc("/facts", handlerer.Facts) - - const port = ":9002" - log.Println("started server on http://localhost" + port) - log.Fatal(http.ListenAndServe(port, nil)) -} - -// private - -func updateFactsWithTicker(ctx context.Context, updateFunc func() error) { - tk := time.NewTicker(updateFactInterval) - go func(c context.Context) { - for { - select { - case <-tk.C: - log.Println("updating...") - if err := updateFunc(); err != nil { - log.Printf("error updating = %v", err) - } - log.Println("updated Successfully") - case <-c.Done(): - return - } - } - }(ctx) -} - -func updateFactsFunc(mf mentalfloss.Mentalfloss, factsRepo *fact.Repository) func() error { - return func() error { - facts, err := mf.Facts() - if err != nil { - log.Fatal("can't reach mentalfloss: ", err) - } - for _, f := range facts { - factsRepo.Add(f) - } - return err - } -} diff --git a/coolfacts-dep/exercise7/mentalfloss/mentalfloss.go b/coolfacts-dep/exercise7/mentalfloss/mentalfloss.go deleted file mode 100644 index c3e127a..0000000 --- a/coolfacts-dep/exercise7/mentalfloss/mentalfloss.go +++ /dev/null @@ -1,57 +0,0 @@ -package mentalfloss - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise7/fact" -) - -type Mentalfloss struct { -} - -func (mf Mentalfloss) Facts() ([]fact.Fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get("http://mentalfloss.com/api/facts") - if err != nil { - return nil, fmt.Errorf("error making request: %v", err) - } - defer resp.Body.Close() - log.Println("got facts from mentalfloss successfully") - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll: %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data: %v", err) - } - - return facts, nil -} - -func parseFromRawItems(b []byte) ([]fact.Fact, error) { - var items []struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` - } - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - var facts []fact.Fact - for _, it := range items { - newFact := fact.Fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts-dep/exercise8/fact/fact.go b/coolfacts-dep/exercise8/fact/fact.go deleted file mode 100644 index 65d33de..0000000 --- a/coolfacts-dep/exercise8/fact/fact.go +++ /dev/null @@ -1,6 +0,0 @@ -package fact - -type Fact struct { - Image string - Description string -} diff --git a/coolfacts-dep/exercise8/fact/service.go b/coolfacts-dep/exercise8/fact/service.go deleted file mode 100644 index 37a82e2..0000000 --- a/coolfacts-dep/exercise8/fact/service.go +++ /dev/null @@ -1,39 +0,0 @@ -package fact - -import ( - "fmt" -) - -type Provider interface { - Facts() ([]Fact, error) -} - -type Repository interface { - Add(f Fact) - GetAll() []Fact -} - -type service struct { - provider Provider - repo Repository -} - -func NewService(p Provider, s Repository) *service { - return &service{ - provider: p, - repo: s, - } -} - -func (s *service) UpdateFacts() error { - facts, err := s.provider.Facts() - if err != nil { - return fmt.Errorf("fact.service.UpdateFacts failed returiev facts %v", err) - } - - for _, fact := range facts { - s.repo.Add(fact) - } - - return nil -} diff --git a/coolfacts-dep/exercise8/go.mod b/coolfacts-dep/exercise8/go.mod deleted file mode 100644 index 970b0a1..0000000 --- a/coolfacts-dep/exercise8/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts/exercise8 - -go 1.13 diff --git a/coolfacts-dep/exercise8/go.sum b/coolfacts-dep/exercise8/go.sum deleted file mode 100644 index c7519f2..0000000 --- a/coolfacts-dep/exercise8/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/FTBpro/go-workshop v0.0.0-20190430114424-99097f9b0d53 h1:JEwdzoVkee+6JmnlW4co6UWKtXpEDLnwcjaIVXe3gXQ= -github.com/FTBpro/go-workshop/coolfacts v0.0.0-20190430114424-99097f9b0d53 h1:U75PlbRup9ERtFo9lbZkW06h3kgEOJXKrFT7ODHmfMM= diff --git a/coolfacts-dep/exercise8/http/handler.go b/coolfacts-dep/exercise8/http/handler.go deleted file mode 100644 index 0e57291..0000000 --- a/coolfacts-dep/exercise8/http/handler.go +++ /dev/null @@ -1,142 +0,0 @@ -package http - -import ( - "encoding/json" - "fmt" - "html/template" - "io/ioutil" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" -) - -type FactRepository interface { - Add(f fact.Fact) - GetAll() []fact.Fact -} - -type factsHandler struct { - factRepo FactRepository -} - -func NewFactsHandler(factRepo FactRepository) *factsHandler { - return &factsHandler{ - factRepo: factRepo, - } -} - -var newsTemplate = ` - - - Coolfacts - - - - -

Hear These Amazing Facts!

- {{ range . }} -
-

{{.Description}}

- -
- {{ end }} - - - -` - -func (h *factsHandler) Ping(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "no http handler found", http.StatusNotFound) - return - } - w.Header().Add("Content-Type", "text/plain") - _, err := fmt.Fprint(w, "PONG") - if err != nil { - errMessage := fmt.Sprintf("error writing response: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - } -} - -func (h *factsHandler) Facts(w http.ResponseWriter, r *http.Request) { - if h.factRepo == nil { - http.Error(w, "fact repo isn't initializes", http.StatusInternalServerError) - } - - switch r.Method { - case http.MethodGet: - h.showFacts(w) - return - case http.MethodPost: - h.postFacts(r, w) - return - default: - http.Error(w, "no http handler found", http.StatusNotFound) - } -} - -func (h *factsHandler) postFacts(r *http.Request, w http.ResponseWriter) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - errMessage := fmt.Sprintf("error read from body: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - var req struct { - Image string `json:"image"` - Description string `json:"description"` - } - err = json.Unmarshal(b, &req) - if err != nil { - errMessage := fmt.Sprintf("error parsing fact: %v", err) - http.Error(w, errMessage, http.StatusBadRequest) - } - f := fact.Fact{ - Image: req.Image, - Description: req.Description, - } - h.factRepo.Add(f) - w.Write([]byte("SUCCESS")) -} - -func (h *factsHandler) showFacts(w http.ResponseWriter) { - w.Header().Add("Content-Type", "text/html") - tmpl, err := template.New("facts").Parse(newsTemplate) - if err != nil { - errMessage := fmt.Sprintf("error ghttp template writing: %v", err) - http.Error(w, errMessage, http.StatusInternalServerError) - return - } - tmpl.Execute(w, h.factRepo.GetAll()) -} diff --git a/coolfacts-dep/exercise8/inmem/factstore.go b/coolfacts-dep/exercise8/inmem/factstore.go deleted file mode 100644 index e08433a..0000000 --- a/coolfacts-dep/exercise8/inmem/factstore.go +++ /dev/null @@ -1,22 +0,0 @@ -package inmem - -import ( - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" -) - -type factRepository struct { - facts []fact.Fact -} - -func NewFactRepository() *factRepository { - return &factRepository{} -} - -func (r *factRepository) Add(f fact.Fact) { - // append is a Go built in function - r.facts = append(r.facts, f) -} - -func (r *factRepository) GetAll() []fact.Fact { - return r.facts -} diff --git a/coolfacts-dep/exercise8/main.go b/coolfacts-dep/exercise8/main.go deleted file mode 100644 index 5966893..0000000 --- a/coolfacts-dep/exercise8/main.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "context" - "log" - "net/http" - "time" - - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" - facthttp "github.com/FTBpro/go-workshop/coolfacts/exercise8/http" - "github.com/FTBpro/go-workshop/coolfacts/exercise8/inmem" - "github.com/FTBpro/go-workshop/coolfacts/exercise8/mentalfloss" -) - -const ( - updateFactInterval = 5 * time.Minute -) - -func main() { - factsRepository := inmem.NewFactRepository() - handler := facthttp.NewFactsHandler(factsRepository) - - mentalflossProvider := mentalfloss.NewProvider() - service := fact.NewService(mentalflossProvider, factsRepository) - if err := service.UpdateFacts(); err != nil { - log.Fatal("couldn't update facts, try later", err.Error()) - } - - ctx, closer := context.WithCancel(context.Background()) - defer closer() - updateFactsWithTicker(ctx, service.UpdateFacts) - - http.HandleFunc("/ping", handler.Ping) - http.HandleFunc("/facts", handler.Facts) - - const port = ":9002" - log.Println("started server on http://localhost" + port) - log.Fatal(http.ListenAndServe(port, nil)) -} - -// private - -func updateFactsWithTicker(ctx context.Context, updateFunc func() error) { - tk := time.NewTicker(updateFactInterval) - go func(c context.Context) { - for { - select { - case <-tk.C: - log.Println("updating...") - if err := updateFunc(); err != nil { - log.Printf("error updating = %v", err) - } - log.Println("updated Successfully") - case <-c.Done(): - return - } - } - }(ctx) -} diff --git a/coolfacts-dep/exercise8/mentalfloss/item.go b/coolfacts-dep/exercise8/mentalfloss/item.go deleted file mode 100644 index 3ce677c..0000000 --- a/coolfacts-dep/exercise8/mentalfloss/item.go +++ /dev/null @@ -1,15 +0,0 @@ -package mentalfloss - -import "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" - -type item struct { - FactText string `json:"fact"` - PrimaryImage string `json:"primaryImage"` -} - -func (it item) ToFact() fact.Fact { - return fact.Fact{ - Image: it.PrimaryImage, - Description: it.FactText, - } -} diff --git a/coolfacts-dep/exercise8/mentalfloss/provider.go b/coolfacts-dep/exercise8/mentalfloss/provider.go deleted file mode 100644 index 08cd96c..0000000 --- a/coolfacts-dep/exercise8/mentalfloss/provider.go +++ /dev/null @@ -1,58 +0,0 @@ -package mentalfloss - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - - "github.com/FTBpro/go-workshop/coolfacts/exercise8/fact" -) - -const getFactsAPI = "http://mentalfloss.com/api/facts" - -type provider struct{} - -func NewProvider() *provider { - return &provider{} -} - -func (p *provider) Facts() ([]fact.Fact, error) { - log.Println("getting facts from mentalfloss") - resp, err := http.Get(getFactsAPI) - if err != nil { - return nil, fmt.Errorf("error get = %v", err) - } - defer resp.Body.Close() - - log.Println("got facts from mentalfloss successfully") - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error readAll = %v", err) - } - - facts, err := parseFromRawItems(b) - if err != nil { - return nil, fmt.Errorf("error parsing data = %v", err) - } - - return facts, nil -} - -// private - -func parseFromRawItems(b []byte) ([]fact.Fact, error) { - items := make([]item, 0) - if err := json.Unmarshal(b, &items); err != nil { - return nil, err - } - - facts := make([]fact.Fact, 0) - for _, it := range items { - newFact := it.ToFact() - facts = append(facts, newFact) - } - - return facts, nil -} diff --git a/coolfacts-dep/go.mod b/coolfacts-dep/go.mod deleted file mode 100644 index 13efe50..0000000 --- a/coolfacts-dep/go.mod +++ /dev/null @@ -1,30 +0,0 @@ -module github.com/FTBpro/go-workshop/coolfacts - -require ( - github.com/FTBpro/go-workshop/coolfacts/exercise1 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise2 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise3 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise4 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise5 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise6 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise7 v0.0.0 - github.com/FTBpro/go-workshop/coolfacts/exercise8 v0.0.0 -) - -replace github.com/FTBpro/go-workshop/coolfacts/exercise1 => ./exercise1 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise2 => ./exercise2 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise3 => ./exercise3 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise4 => ./exercise4 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise5 => ./exercise5 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise6 => ./exercise6 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise7 => ./exercise7 - -replace github.com/FTBpro/go-workshop/coolfacts/exercise8 => ./exercise8 - -go 1.13 diff --git a/coolfacts-dep/styles.css b/coolfacts-dep/styles.css deleted file mode 100644 index a30fb11..0000000 --- a/coolfacts-dep/styles.css +++ /dev/null @@ -1,30 +0,0 @@ -/* This file is not magically included anywhere*/ -body { - font-family: Helvetica, Arial, sans-serif; - color: #26323d; - max-width: 720px; - margin: auto; -} - -article { - border: 1px solid #0095c4; - border-radius: 4px; - max-width: 320px; - text-align: center; -} - -article h3 { - font-weight: normal; -} - -article a { - color: #26323d; -} - -article a:hover { - color: #f16957; -} - -article img { - border-radius: 4px; -} From d4823d266d659930eb648d637fa758d439463425 Mon Sep 17 00:00:00 2001 From: Oren Rosen Date: Tue, 6 Dec 2022 14:29:43 +0200 Subject: [PATCH 08/10] goimprots --- gopresent/interfaces/example1/main.go | 8 +++++--- gopresent/interfaces/example1/something/something.go | 4 ++-- gopresent/interfaces/example1/speaker.go | 3 ++- gopresent/pointers/value1.go | 4 +--- gopresent/pointers/value2.go | 6 ++---- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/gopresent/interfaces/example1/main.go b/gopresent/interfaces/example1/main.go index c445f00..4cba19d 100644 --- a/gopresent/interfaces/example1/main.go +++ b/gopresent/interfaces/example1/main.go @@ -1,7 +1,7 @@ package main import "fmt" - + type Action interface { Do(int, int) int } @@ -10,14 +10,15 @@ func DoSomething(x, y int, action Action) { fmt.Printf("x = %d, y = %d, action.Do(x, y) = %d\n", x, y, action.Do(x, y)) } - // start OMIT type adder struct{} + func (a adder) Do(x, y int) int { return x + y } type multiplier struct{} + func (m multiplier) Do(x, y int) int { return x * y } @@ -26,4 +27,5 @@ func main() { DoSomething(3, 4, adder{}) DoSomething(3, 4, multiplier{}) } -// end OMIT \ No newline at end of file + +// end OMIT diff --git a/gopresent/interfaces/example1/something/something.go b/gopresent/interfaces/example1/something/something.go index e483ba6..974431a 100644 --- a/gopresent/interfaces/example1/something/something.go +++ b/gopresent/interfaces/example1/something/something.go @@ -4,11 +4,11 @@ import ( "fmt" ) -type Actioner interface{ +type Actioner interface { Do(int, int) int } func Do(x, y int, action Actioner) { result := action.Do(x, y) fmt.Printf("x = %d, y = %d, action.Do(x, y) = %d\n", x, y, result) -} \ No newline at end of file +} diff --git a/gopresent/interfaces/example1/speaker.go b/gopresent/interfaces/example1/speaker.go index 9283b0c..0557e8f 100644 --- a/gopresent/interfaces/example1/speaker.go +++ b/gopresent/interfaces/example1/speaker.go @@ -1,8 +1,9 @@ package main type Lala string + // type speaker struct{} // func (s speaker) Speak() { // fmt.Println("Hello, this is speaker") -// } \ No newline at end of file +// } diff --git a/gopresent/pointers/value1.go b/gopresent/pointers/value1.go index b4c341c..7696e6a 100644 --- a/gopresent/pointers/value1.go +++ b/gopresent/pointers/value1.go @@ -4,7 +4,7 @@ import ( "fmt" ) -func main(){ +func main() { a := 42 b := a // HL @@ -13,5 +13,3 @@ func main(){ fmt.Printf("value of a:\t%d\n", a) fmt.Printf("address of a:\t%p\n", &a) } - - diff --git a/gopresent/pointers/value2.go b/gopresent/pointers/value2.go index 23f2be5..dd81aab 100644 --- a/gopresent/pointers/value2.go +++ b/gopresent/pointers/value2.go @@ -4,9 +4,9 @@ import ( "fmt" ) -func main(){ +func main() { a := 42 - do := func (b int) { // HL + do := func(b int) { // HL fmt.Printf("value of b:\t%d\n", b) fmt.Printf("address of b:\t%p\n", &b) } @@ -15,5 +15,3 @@ func main(){ fmt.Printf("value of a:\t%d\n", a) fmt.Printf("address of a:\t%p\n", &a) } - - From f41935cc1e0451b52bec442600b2a50e50032924 Mon Sep 17 00:00:00 2001 From: Oren Rosen Date: Fri, 9 Dec 2022 16:58:39 +0200 Subject: [PATCH 09/10] main --- coolfacts/main.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/coolfacts/main.go b/coolfacts/main.go index c325f56..8f61880 100644 --- a/coolfacts/main.go +++ b/coolfacts/main.go @@ -1,5 +1,9 @@ package main +import ( + "fmt" +) + func main() { - // TODO: Print Hello, World! + fmt.Println("Hello, world!") } From 6a6fe40343e4125a67c6922332dca59910133595 Mon Sep 17 00:00:00 2001 From: Oren Rosen Date: Sat, 10 Dec 2022 11:27:09 +0200 Subject: [PATCH 10/10] remove answer --- coolfacts/main.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/coolfacts/main.go b/coolfacts/main.go index 8f61880..da29a2c 100644 --- a/coolfacts/main.go +++ b/coolfacts/main.go @@ -1,9 +1,4 @@ package main -import ( - "fmt" -) - func main() { - fmt.Println("Hello, world!") }