Add basic go client

This commit is contained in:
simon987 2019-03-30 11:17:28 -04:00
parent 26dee89672
commit e7672f47ed
7 changed files with 315 additions and 32 deletions

158
client/client.go Normal file
View File

@ -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)
}
}

42
client/models.go Normal file
View File

@ -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"`
}

View File

@ -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)
}

View File

@ -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

View File

@ -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

105
test/client_test.go Normal file
View File

@ -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()
}
}

View File

@ -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"`