diff --git a/coolfacts/cmd/coolfacts_client/client.go b/coolfacts/cmd/coolfacts_client/client.go new file mode 100644 index 0000000..b46a4bd --- /dev/null +++ b/coolfacts/cmd/coolfacts_client/client.go @@ -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) +} diff --git a/coolfacts/cmd/coolfacts_client/main.go b/coolfacts/cmd/coolfacts_client/main.go new file mode 100644 index 0000000..fa7fbcd --- /dev/null +++ b/coolfacts/cmd/coolfacts_client/main.go @@ -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") + } +}