Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions coolfacts/cmd/coolfacts_client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package main

import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"

"github.com/FTBpro/go-workshop/coolfacts/coolfact"
)

const (
pathGetFacts = "/facts"
)

type getFactsResponse struct {
// TODO: add fields
// This struct represent the createFact API response body of the server.
// We will decode the response into a variable of this struct type.
// Since the server response is json, we will use json decode method.
// For this be sure to add json tags on the struct. (https://gobyexample.com/json)
// The response body is:
// {
// "facts": [
// {
// "topic": "...",
// "description": "...",
// }
// ...
// ]
// }
//
}

func (r getFactsResponse) ToCoolFacts() []coolfact.Fact {
// TODO: implement
// loop over the response facts and convert them to the entity type []coolfact.Fact
}

type client struct {
endpoint string
httpClient *http.Client
}

func NewClient(endpoint string) *client {
return &client{
endpoint: endpoint,
httpClient: &http.Client{},
}
}

func (c *client) GetFacts() ([]coolfact.Fact, error) {
ul := c.endpoint + pathGetFacts
res, err := c.httpClient.Get(ul)
if err != nil {
return nil, fmt.Errorf("client.GetLastCreatedFact to do request: %v", err)
}

// The client must close the body after the response is handled
// We must read all the body before closing it, so for reading the body and copying to ioutil.Discard, which does nothing
defer func() {
if res != nil && res.Body != nil {
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
}
}()

// TODO: handle response
// this method returns *http.Response.
// - If response status code isn't 200 (http.StatusOK), you should read the error from the response.
// use method c.readError which is already implemented.
// - If the response is OK, use method readResponseGetFacts (which you will implement) to return the facts
}

type errorResponse struct {
Error string `json:"error"`
}

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", err)
}

return errRes.Error, nil
}

func (c *client) readResponseGetFacts(res *http.Response) (getFactsResponse, error) {
// TODO: implement - decode the json response into the target
// Use variable of type getFactsResponse.
// Use json.NewDecoder(...).Decode(...) (unlike the decoding in readError method)
}
74 changes: 74 additions & 0 deletions coolfacts/cmd/coolfacts_client/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package main

import (
"bufio"
"errors"
"fmt"
"log"
"os"
"regexp"
"strings"
)

const (
serverEndpoint = "http://127.0.0.1:9002"

commandGetFacts = "getFacts"
)

func main() {
fmt.Println("Hello, Client!")

cl := NewClient(serverEndpoint)

reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("> ")
input, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}

input = strings.Trim(input, "\n ")
tokens := regexp.MustCompile("[ ]+").Split(input, -1)

cmd, args := tokens[0], tokens[1:]
if cmd == "exit" {
fmt.Println("Bye, bye!")
return
}

res, err := processCmd(cl, cmd, args)
if err != nil {
fmt.Println("ERROR:", err)
continue
}

if res != "" {
fmt.Println(res)
}
}

}

func processCmd(cl *client, cmd string, args []string) (string, error) {
switch cmd {
case "":
return "", nil
case commandGetFacts:
facts, err := cl.GetFacts()
if err != nil {
return "", err
}

var msg string
for i, fact := range facts {
msg += fmt.Sprintf("\n**************\nFact %d:", i)
msg += fmt.Sprintf("\tTopic: %s\n\tDescription: %s\n", fact.Topic, fact.Description)
}

return msg, nil
default:
return "", errors.New("unknown command")
}
}