From e7672f47edad707f515f6de29623d20a5940c03c Mon Sep 17 00:00:00 2001 From: simon987 Date: Sat, 30 Mar 2019 11:17:28 -0400 Subject: [PATCH] Add basic go client --- client/client.go | 158 ++++++++++++++++++++++++++++++++++++++++ client/models.go | 42 +++++++++++ test/api_rate_test.go | 3 +- test/api_task_test.go | 7 +- test/api_worker_test.go | 7 +- test/client_test.go | 105 ++++++++++++++++++++++++++ test/common.go | 25 ------- 7 files changed, 315 insertions(+), 32 deletions(-) create mode 100644 client/client.go create mode 100644 client/models.go create mode 100644 test/client_test.go diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..575ab0f --- /dev/null +++ b/client/client.go @@ -0,0 +1,158 @@ +package client + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "github.com/simon987/task_tracker/api" + "io/ioutil" + "net/http" + "strconv" +) + +type taskTrackerClient struct { + worker *Worker + httpClient http.Client + serverAddress string + + secretB64 string + workerIdStr string +} + +func New(serverAddress string) *taskTrackerClient { + + client := new(taskTrackerClient) + client.serverAddress = serverAddress + + return client +} + +func (c *taskTrackerClient) SetWorker(worker *Worker) { + c.worker = worker + c.secretB64 = base64.StdEncoding.EncodeToString(worker.Secret) + c.workerIdStr = strconv.FormatInt(worker.Id, 10) +} + +func (c *taskTrackerClient) get(path string) *http.Response { + + url := "http://" + c.serverAddress + path + req, err := http.NewRequest("GET", url, nil) + + if c.worker != nil { + req.Header.Add("X-Worker-Id", c.workerIdStr) + req.Header.Add("X-Secret", c.secretB64) + } + + r, err := c.httpClient.Do(req) + handleErr(err) + + return r +} + +func (c *taskTrackerClient) post(path string, x interface{}) *http.Response { + + body, err := json.Marshal(x) + buf := bytes.NewBuffer(body) + + req, err := http.NewRequest("POST", "http://"+c.serverAddress+path, buf) + handleErr(err) + + if c.worker != nil { + req.Header.Add("X-Worker-Id", c.workerIdStr) + req.Header.Add("X-Secret", c.secretB64) + } + + r, err := c.httpClient.Do(req) + handleErr(err) + + return r +} + +func unmarshalResponse(r *http.Response, result interface{}) error { + data, err := ioutil.ReadAll(r.Body) + if err != nil { + return err + } + fmt.Println(string(data)) + err = json.Unmarshal(data, result) + if err != nil { + return err + } + return nil +} + +func (c taskTrackerClient) MakeWorker(alias string) (*Worker, error) { + + httpResp := c.post("/worker/create", api.CreateWorkerRequest{ + Alias: alias, + }) + var jsonResp CreateWorkerResponse + err := unmarshalResponse(httpResp, &jsonResp) + + if err == nil { + clientWorker := Worker{ + Alias: jsonResp.Content.Worker.Alias, + Secret: jsonResp.Content.Worker.Secret, + Id: jsonResp.Content.Worker.Id, + } + return &clientWorker, nil + } + + return nil, err +} + +func (c taskTrackerClient) FetchTask(projectId int) (*AssignTaskResponse, error) { + + httpResp := c.get("/task/get/" + strconv.Itoa(projectId)) + var jsonResp AssignTaskResponse + err := unmarshalResponse(httpResp, &jsonResp) + + //TODO: Handle rate limiting here? + + return &jsonResp, err +} + +func (c taskTrackerClient) ReleaseTask(req api.ReleaseTaskRequest) (*ReleaseTaskResponse, error) { + + httpResp := c.post("/task/release", req) + var jsonResp ReleaseTaskResponse + err := unmarshalResponse(httpResp, &jsonResp) + + return &jsonResp, err +} + +func (c taskTrackerClient) SubmitTask(req api.SubmitTaskRequest) (AssignTaskResponse, error) { + + httpResp := c.post("/task/submit", req) + var jsonResp AssignTaskResponse + err := unmarshalResponse(httpResp, &jsonResp) + + //TODO: Handle rate limiting here? + + return jsonResp, err +} + +func (c taskTrackerClient) GetProjectSecret(projectId int) (string, error) { + + httpResp := c.get("/project/secret/" + strconv.Itoa(projectId)) + var jsonResp ProjectSecretResponse + err := unmarshalResponse(httpResp, &jsonResp) + + return jsonResp.Content.Secret, err +} + +func (c taskTrackerClient) RequestAccess(req api.CreateWorkerAccessRequest) (api.JsonResponse, error) { + + httpResp := c.post("/project/request_access", req) + var jsonResp api.JsonResponse + err := unmarshalResponse(httpResp, &jsonResp) + + return jsonResp, err +} + +func handleErr(err error) { + if err != nil { + panic(err) + } +} diff --git a/client/models.go b/client/models.go new file mode 100644 index 0000000..3bf1c0e --- /dev/null +++ b/client/models.go @@ -0,0 +1,42 @@ +package client + +import "github.com/simon987/task_tracker/storage" + +type Worker struct { + Id int64 `json:"id"` + Alias string `json:"alias,omitempty"` + Secret []byte `json:"secret"` +} + +type CreateWorkerResponse struct { + Ok bool `json:"ok"` + Message string `json:"message"` + Content struct { + Worker *storage.Worker `json:"worker"` + } `json:"content"` +} + +type AssignTaskResponse struct { + Ok bool `json:"ok"` + Message string `json:"message"` + RateLimitDelay float64 `json:"rate_limit_delay,omitempty"` + Content struct { + Task *storage.Task `json:"task"` + } `json:"content"` +} + +type ReleaseTaskResponse struct { + Ok bool `json:"ok"` + Message string `json:"message"` + Content struct { + Updated bool `json:"updated"` + } `json:"content"` +} + +type ProjectSecretResponse struct { + Ok bool `json:"ok"` + Message string `json:"message"` + Content struct { + Secret string `json:"secret"` + } `json:"content"` +} diff --git a/test/api_rate_test.go b/test/api_rate_test.go index 95d7d3e..488c74e 100644 --- a/test/api_rate_test.go +++ b/test/api_rate_test.go @@ -3,6 +3,7 @@ package test import ( "fmt" "github.com/simon987/task_tracker/api" + "github.com/simon987/task_tracker/client" "testing" ) @@ -31,7 +32,7 @@ func TestAssignRateLimit(t *testing.T) { }, w) } - var lastResp TaskAR + var lastResp client.AssignTaskResponse for i := 0; i < 3; i++ { lastResp = getTaskFromProject(project, w) } diff --git a/test/api_task_test.go b/test/api_task_test.go index 55809dd..51f209d 100644 --- a/test/api_task_test.go +++ b/test/api_task_test.go @@ -3,6 +3,7 @@ package test import ( "fmt" "github.com/simon987/task_tracker/api" + "github.com/simon987/task_tracker/client" "github.com/simon987/task_tracker/storage" "math" "testing" @@ -925,19 +926,19 @@ func createTask(request api.SubmitTaskRequest, worker *storage.Worker) (ar api.J return } -func getTask(worker *storage.Worker) (ar TaskAR) { +func getTask(worker *storage.Worker) (ar client.AssignTaskResponse) { r := Get("/task/get", worker, nil) UnmarshalResponse(r, &ar) return } -func getTaskFromProject(project int64, worker *storage.Worker) (ar TaskAR) { +func getTaskFromProject(project int64, worker *storage.Worker) (ar client.AssignTaskResponse) { r := Get(fmt.Sprintf("/task/get/%d", project), worker, nil) UnmarshalResponse(r, &ar) return } -func releaseTask(request api.ReleaseTaskRequest, worker *storage.Worker) (ar ReleaseAR) { +func releaseTask(request api.ReleaseTaskRequest, worker *storage.Worker) (ar client.ReleaseTaskResponse) { r := Post("/task/release", request, worker, nil) UnmarshalResponse(r, &ar) return diff --git a/test/api_worker_test.go b/test/api_worker_test.go index 0622b76..2f62e83 100644 --- a/test/api_worker_test.go +++ b/test/api_worker_test.go @@ -3,6 +3,7 @@ package test import ( "fmt" "github.com/simon987/task_tracker/api" + "github.com/simon987/task_tracker/client" "github.com/simon987/task_tracker/storage" "net/http" "testing" @@ -108,13 +109,13 @@ func TestInvalidAccessRequest(t *testing.T) { } } -func createWorker(req api.CreateWorkerRequest) (ar WorkerAR) { +func createWorker(req api.CreateWorkerRequest) (ar client.CreateWorkerResponse) { r := Post("/worker/create", req, nil, nil) UnmarshalResponse(r, &ar) return } -func getWorker(id int64) (ar WorkerAR) { +func getWorker(id int64) (ar client.CreateWorkerResponse) { r := Get(fmt.Sprintf("/worker/get/%d", id), nil, nil) UnmarshalResponse(r, &ar) return @@ -125,7 +126,7 @@ func genWid() *storage.Worker { return resp.Content.Worker } -func requestAccess(req api.CreateWorkerAccessRequest, w *storage.Worker) (ar WorkerAR) { +func requestAccess(req api.CreateWorkerAccessRequest, w *storage.Worker) (ar client.CreateWorkerResponse) { r := Post(fmt.Sprintf("/project/request_access"), req, w, nil) UnmarshalResponse(r, &ar) return diff --git a/test/client_test.go b/test/client_test.go new file mode 100644 index 0000000..5300f27 --- /dev/null +++ b/test/client_test.go @@ -0,0 +1,105 @@ +package test + +import ( + "github.com/simon987/task_tracker/api" + "github.com/simon987/task_tracker/client" + "github.com/simon987/task_tracker/config" + "github.com/simon987/task_tracker/storage" + "testing" +) + +func TestClientMakeWorker(t *testing.T) { + + c := client.New(config.Cfg.ServerAddr) + w, err := c.MakeWorker("test") + + if err != nil { + t.Error() + } + + if w.Alias != "test" { + t.Error() + } +} + +func TestClientFetchTaskNoTaskAvailable(t *testing.T) { + + c := client.New(config.Cfg.ServerAddr) + w, _ := c.MakeWorker("test") + c.SetWorker(w) + + _, err := c.FetchTask(89988) + + if err != nil { + t.Error() + } +} + +func TestClientFetchTask(t *testing.T) { + + c := client.New(config.Cfg.ServerAddr) + w, _ := c.MakeWorker("test") + c.SetWorker(w) + + createTask(api.SubmitTaskRequest{ + Project: testProject, + Recipe: " ", + }, testWorker) + + requestAccess(api.CreateWorkerAccessRequest{ + Project: testProject, + Submit: false, + Assign: true, + }, &storage.Worker{ + Secret: w.Secret, + Id: w.Id, + }) + acceptAccessRequest(testProject, w.Id, testAdminCtx) + + resp, err := c.FetchTask(int(testProject)) + + if err != nil { + t.Error() + } + + if resp.Content.Task == nil { + t.Error() + } +} + +func TestClientReleaseTask(t *testing.T) { + + c := client.New(config.Cfg.ServerAddr) + w, _ := c.MakeWorker("test") + c.SetWorker(w) + + createTask(api.SubmitTaskRequest{ + Project: testProject, + Recipe: " ", + }, testWorker) + + requestAccess(api.CreateWorkerAccessRequest{ + Project: testProject, + Submit: false, + Assign: true, + }, &storage.Worker{ + Secret: w.Secret, + Id: w.Id, + }) + acceptAccessRequest(testProject, w.Id, testAdminCtx) + + fetchResp, _ := c.FetchTask(int(testProject)) + + resp, err := c.ReleaseTask(api.ReleaseTaskRequest{ + TaskId: fetchResp.Content.Task.Id, + Result: storage.TR_OK, + }) + + if err != nil { + t.Error() + } + + if resp.Content.Updated != true { + t.Error() + } +} diff --git a/test/common.go b/test/common.go index d5588b9..8ad9df4 100644 --- a/test/common.go +++ b/test/common.go @@ -94,14 +94,6 @@ func UnmarshalResponse(r *http.Response, result interface{}) { handleErr(err) } -type WorkerAR struct { - Ok bool `json:"ok"` - Message string `json:"message"` - Content struct { - Worker *storage.Worker `json:"worker"` - } `json:"content"` -} - type RegisterAR struct { Ok bool `json:"ok"` Message string `json:"message"` @@ -148,23 +140,6 @@ type ProjectListAR struct { } `json:"content"` } -type TaskAR struct { - Ok bool `json:"ok"` - Message string `json:"message"` - RateLimitDelay float64 `json:"rate_limit_delay,omitempty"` - Content struct { - Task *storage.Task `json:"task"` - } `json:"content"` -} - -type ReleaseAR struct { - Ok bool `json:"ok"` - Message string `json:"message"` - Content struct { - Updated bool `json:"updated"` - } `json:"content"` -} - type WebhookSecretAR struct { Ok bool `json:"ok"` Message string `json:"message"`