mirror of
				https://github.com/simon987/task_tracker.git
				synced 2025-11-04 09:46:52 +00:00 
			
		
		
		
	added optional task unique field
This commit is contained in:
		
							parent
							
								
									f250a2180c
								
							
						
					
					
						commit
						64152bfc08
					
				@ -72,7 +72,8 @@ func (api *WebAPI) ReceiveGitWebHook(r *Request) {
 | 
				
			|||||||
	version := getVersion(payload)
 | 
						version := getVersion(payload)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	project.Version = version
 | 
						project.Version = version
 | 
				
			||||||
	api.Database.UpdateProject(project)
 | 
						err := api.Database.UpdateProject(project)
 | 
				
			||||||
 | 
						handleErr(err, r)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func signatureValid(r *Request) (matches bool) {
 | 
					func signatureValid(r *Request) (matches bool) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										16
									
								
								api/main.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								api/main.go
									
									
									
									
									
								
							@ -1,6 +1,7 @@
 | 
				
			|||||||
package api
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"github.com/Sirupsen/logrus"
 | 
						"github.com/Sirupsen/logrus"
 | 
				
			||||||
	"github.com/buaazp/fasthttprouter"
 | 
						"github.com/buaazp/fasthttprouter"
 | 
				
			||||||
	"github.com/valyala/fasthttp"
 | 
						"github.com/valyala/fasthttp"
 | 
				
			||||||
@ -48,6 +49,7 @@ func New() *WebAPI {
 | 
				
			|||||||
	api.router.POST("/log/error", LogRequestMiddleware(LogError))
 | 
						api.router.POST("/log/error", LogRequestMiddleware(LogError))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	api.router.POST("/worker/create", LogRequestMiddleware(api.WorkerCreate))
 | 
						api.router.POST("/worker/create", LogRequestMiddleware(api.WorkerCreate))
 | 
				
			||||||
 | 
						api.router.POST("/worker/update", LogRequestMiddleware(api.WorkerUpdate))
 | 
				
			||||||
	api.router.GET("/worker/get/:id", LogRequestMiddleware(api.WorkerGet))
 | 
						api.router.GET("/worker/get/:id", LogRequestMiddleware(api.WorkerGet))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	api.router.POST("/access/grant", LogRequestMiddleware(api.WorkerGrantAccess))
 | 
						api.router.POST("/access/grant", LogRequestMiddleware(api.WorkerGrantAccess))
 | 
				
			||||||
@ -55,6 +57,7 @@ func New() *WebAPI {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	api.router.POST("/project/create", LogRequestMiddleware(api.ProjectCreate))
 | 
						api.router.POST("/project/create", LogRequestMiddleware(api.ProjectCreate))
 | 
				
			||||||
	api.router.GET("/project/get/:id", LogRequestMiddleware(api.ProjectGet))
 | 
						api.router.GET("/project/get/:id", LogRequestMiddleware(api.ProjectGet))
 | 
				
			||||||
 | 
						api.router.POST("/project/update/:id", LogRequestMiddleware(api.ProjectUpdate))
 | 
				
			||||||
	api.router.GET("/project/stats/:id", LogRequestMiddleware(api.ProjectGetStats))
 | 
						api.router.GET("/project/stats/:id", LogRequestMiddleware(api.ProjectGetStats))
 | 
				
			||||||
	api.router.GET("/project/stats", LogRequestMiddleware(api.ProjectGetAllStats))
 | 
						api.router.GET("/project/stats", LogRequestMiddleware(api.ProjectGetAllStats))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -67,6 +70,19 @@ func New() *WebAPI {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	api.router.POST("/logs", LogRequestMiddleware(api.GetLog))
 | 
						api.router.POST("/logs", LogRequestMiddleware(api.GetLog))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						api.router.NotFound = func(ctx *fasthttp.RequestCtx) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ctx.Request.Header.IsOptions() {
 | 
				
			||||||
 | 
								ctx.Response.Header.Add("Access-Control-Allow-Headers", "Content-Type")
 | 
				
			||||||
 | 
								ctx.Response.Header.Add("Access-Control-Allow-Methods", "GET, POST, OPTION")
 | 
				
			||||||
 | 
								ctx.Response.Header.Add("Access-Control-Allow-Origin", "*")
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.SetStatusCode(404)
 | 
				
			||||||
 | 
								_, _ = fmt.Fprintf(ctx, "Not found")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return api
 | 
						return api
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,20 @@ type CreateProjectRequest struct {
 | 
				
			|||||||
	Public   bool   `json:"public"`
 | 
						Public   bool   `json:"public"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UpdateProjectRequest struct {
 | 
				
			||||||
 | 
						Name     string `json:"name"`
 | 
				
			||||||
 | 
						CloneUrl string `json:"clone_url"`
 | 
				
			||||||
 | 
						GitRepo  string `json:"git_repo"`
 | 
				
			||||||
 | 
						Priority int64  `json:"priority"`
 | 
				
			||||||
 | 
						Motd     string `json:"motd"`
 | 
				
			||||||
 | 
						Public   bool   `json:"public"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UpdateProjectResponse struct {
 | 
				
			||||||
 | 
						Ok      bool   `json:"ok"`
 | 
				
			||||||
 | 
						Message string `json:"message,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CreateProjectResponse struct {
 | 
					type CreateProjectResponse struct {
 | 
				
			||||||
	Ok      bool   `json:"ok"`
 | 
						Ok      bool   `json:"ok"`
 | 
				
			||||||
	Id      int64  `json:"id,omitempty"`
 | 
						Id      int64  `json:"id,omitempty"`
 | 
				
			||||||
@ -86,10 +100,66 @@ func (api *WebAPI) ProjectCreate(r *Request) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (api *WebAPI) ProjectUpdate(r *Request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64)
 | 
				
			||||||
 | 
						handleErr(err, r) //todo handle invalid id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						updateReq := &UpdateProjectRequest{}
 | 
				
			||||||
 | 
						if r.GetJson(updateReq) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							project := &storage.Project{
 | 
				
			||||||
 | 
								Id:       id,
 | 
				
			||||||
 | 
								Name:     updateReq.Name,
 | 
				
			||||||
 | 
								CloneUrl: updateReq.CloneUrl,
 | 
				
			||||||
 | 
								GitRepo:  updateReq.GitRepo,
 | 
				
			||||||
 | 
								Priority: updateReq.Priority,
 | 
				
			||||||
 | 
								Motd:     updateReq.Motd,
 | 
				
			||||||
 | 
								Public:   updateReq.Public,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if isValidProject(project) {
 | 
				
			||||||
 | 
								err := api.Database.UpdateProject(project)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									r.Json(CreateProjectResponse{
 | 
				
			||||||
 | 
										Ok:      false,
 | 
				
			||||||
 | 
										Message: err.Error(),
 | 
				
			||||||
 | 
									}, 500)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									logrus.WithError(err).WithFields(logrus.Fields{
 | 
				
			||||||
 | 
										"project": project,
 | 
				
			||||||
 | 
									}).Warn("Error during project update")
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									r.OkJson(UpdateProjectResponse{
 | 
				
			||||||
 | 
										Ok: true,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									logrus.WithFields(logrus.Fields{
 | 
				
			||||||
 | 
										"project": project,
 | 
				
			||||||
 | 
									}).Debug("Updated project")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								logrus.WithFields(logrus.Fields{
 | 
				
			||||||
 | 
									"project": project,
 | 
				
			||||||
 | 
								}).Warn("Invalid project")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								r.Json(CreateProjectResponse{
 | 
				
			||||||
 | 
									Ok:      false,
 | 
				
			||||||
 | 
									Message: "Invalid project",
 | 
				
			||||||
 | 
								}, 400)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func isValidProject(project *storage.Project) bool {
 | 
					func isValidProject(project *storage.Project) bool {
 | 
				
			||||||
	if len(project.Name) <= 0 {
 | 
						if len(project.Name) <= 0 {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if project.Priority < 0 {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -97,7 +167,7 @@ func isValidProject(project *storage.Project) bool {
 | 
				
			|||||||
func (api *WebAPI) ProjectGet(r *Request) {
 | 
					func (api *WebAPI) ProjectGet(r *Request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64)
 | 
						id, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64)
 | 
				
			||||||
	handleErr(err, r)
 | 
						handleErr(err, r) //todo handle invalid id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	project := api.Database.GetProject(id)
 | 
						project := api.Database.GetProject(id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										73
									
								
								api/task.go
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								api/task.go
									
									
									
									
									
								
							@ -1,8 +1,13 @@
 | 
				
			|||||||
package api
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"crypto"
 | 
				
			||||||
 | 
						"crypto/hmac"
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"github.com/Sirupsen/logrus"
 | 
						"github.com/Sirupsen/logrus"
 | 
				
			||||||
 | 
						"github.com/dchest/siphash"
 | 
				
			||||||
	"github.com/google/uuid"
 | 
						"github.com/google/uuid"
 | 
				
			||||||
	"src/task_tracker/storage"
 | 
						"src/task_tracker/storage"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
@ -14,12 +19,13 @@ type CreateTaskRequest struct {
 | 
				
			|||||||
	Recipe        string `json:"recipe"`
 | 
						Recipe        string `json:"recipe"`
 | 
				
			||||||
	Priority      int64  `json:"priority"`
 | 
						Priority      int64  `json:"priority"`
 | 
				
			||||||
	MaxAssignTime int64  `json:"max_assign_time"`
 | 
						MaxAssignTime int64  `json:"max_assign_time"`
 | 
				
			||||||
 | 
						Hash64        int64  `json:"hash_u64"`
 | 
				
			||||||
 | 
						UniqueString  string `json:"unique_string"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ReleaseTaskRequest struct {
 | 
					type ReleaseTaskRequest struct {
 | 
				
			||||||
	TaskId  int64 `json:"task_id"`
 | 
						TaskId  int64 `json:"task_id"`
 | 
				
			||||||
	Success bool  `json:"success"`
 | 
						Success bool  `json:"success"`
 | 
				
			||||||
	WorkerId *uuid.UUID `json:"worker_id"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ReleaseTaskResponse struct {
 | 
					type ReleaseTaskResponse struct {
 | 
				
			||||||
@ -51,8 +57,14 @@ func (api *WebAPI) TaskCreate(r *Request) {
 | 
				
			|||||||
			MaxAssignTime: createReq.MaxAssignTime,
 | 
								MaxAssignTime: createReq.MaxAssignTime,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if isTaskValid(task) {
 | 
							if createReq.IsValid() && isTaskValid(task) {
 | 
				
			||||||
			err := api.Database.SaveTask(task, createReq.Project)
 | 
					
 | 
				
			||||||
 | 
								if createReq.UniqueString != "" {
 | 
				
			||||||
 | 
									//TODO: Load key from config
 | 
				
			||||||
 | 
									createReq.Hash64 = int64(siphash.Hash(1, 2, []byte(createReq.UniqueString)))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err := api.Database.SaveTask(task, createReq.Project, createReq.Hash64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				r.Json(CreateTaskResponse{
 | 
									r.Json(CreateTaskResponse{
 | 
				
			||||||
@ -76,6 +88,10 @@ func (api *WebAPI) TaskCreate(r *Request) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (req *CreateTaskRequest) IsValid() bool {
 | 
				
			||||||
 | 
						return req.Hash64 == 0 || req.UniqueString == ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func isTaskValid(task *storage.Task) bool {
 | 
					func isTaskValid(task *storage.Task) bool {
 | 
				
			||||||
	if task.MaxRetries < 0 {
 | 
						if task.MaxRetries < 0 {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
@ -89,7 +105,7 @@ func isTaskValid(task *storage.Task) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (api *WebAPI) TaskGetFromProject(r *Request) {
 | 
					func (api *WebAPI) TaskGetFromProject(r *Request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	worker, err := api.workerFromQueryArgs(r)
 | 
						worker, err := api.validateSignature(r)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		r.Json(GetTaskResponse{
 | 
							r.Json(GetTaskResponse{
 | 
				
			||||||
			Ok:      false,
 | 
								Ok:      false,
 | 
				
			||||||
@ -121,7 +137,7 @@ func (api *WebAPI) TaskGetFromProject(r *Request) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (api *WebAPI) TaskGet(r *Request) {
 | 
					func (api *WebAPI) TaskGet(r *Request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	worker, err := api.workerFromQueryArgs(r)
 | 
						worker, err := api.validateSignature(r)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		r.Json(GetTaskResponse{
 | 
							r.Json(GetTaskResponse{
 | 
				
			||||||
			Ok:      false,
 | 
								Ok:      false,
 | 
				
			||||||
@ -138,9 +154,11 @@ func (api *WebAPI) TaskGet(r *Request) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (api WebAPI) workerFromQueryArgs(r *Request) (*storage.Worker, error) {
 | 
					func (api WebAPI) validateSignature(r *Request) (*storage.Worker, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						widStr := string(r.Ctx.Request.Header.Peek("X-Worker-Id"))
 | 
				
			||||||
 | 
						signature := r.Ctx.Request.Header.Peek("X-Signature")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	widStr := string(r.Ctx.QueryArgs().Peek("wid"))
 | 
					 | 
				
			||||||
	wid, err := uuid.Parse(widStr)
 | 
						wid, err := uuid.Parse(widStr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logrus.WithError(err).WithFields(logrus.Fields{
 | 
							logrus.WithError(err).WithFields(logrus.Fields{
 | 
				
			||||||
@ -155,20 +173,53 @@ func (api WebAPI) workerFromQueryArgs(r *Request) (*storage.Worker, error) {
 | 
				
			|||||||
	if worker == nil {
 | 
						if worker == nil {
 | 
				
			||||||
		logrus.WithError(err).WithFields(logrus.Fields{
 | 
							logrus.WithError(err).WithFields(logrus.Fields{
 | 
				
			||||||
			"wid": widStr,
 | 
								"wid": widStr,
 | 
				
			||||||
		}).Warn("Can't parse wid")
 | 
							}).Warn("Worker id does not match any valid worker")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return nil, errors.New("worker id does not match any valid worker")
 | 
							return nil, errors.New("worker id does not match any valid worker")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var body []byte
 | 
				
			||||||
 | 
						if r.Ctx.Request.Header.IsGet() {
 | 
				
			||||||
 | 
							body = r.Ctx.Request.RequestURI()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							body = r.Ctx.Request.Body()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mac := hmac.New(crypto.SHA256.New, worker.Secret)
 | 
				
			||||||
 | 
						mac.Write(body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						expectedMac := make([]byte, 64)
 | 
				
			||||||
 | 
						hex.Encode(expectedMac, mac.Sum(nil))
 | 
				
			||||||
 | 
						matches := bytes.Compare(expectedMac, signature) == 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logrus.WithFields(logrus.Fields{
 | 
				
			||||||
 | 
							"expected":  string(expectedMac),
 | 
				
			||||||
 | 
							"signature": string(signature),
 | 
				
			||||||
 | 
							"matches":   matches,
 | 
				
			||||||
 | 
						}).Trace("Validating Worker signature")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !matches {
 | 
				
			||||||
 | 
							return nil, errors.New("invalid signature")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return worker, nil
 | 
						return worker, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (api *WebAPI) TaskRelease(r *Request) {
 | 
					func (api *WebAPI) TaskRelease(r *Request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req := ReleaseTaskRequest{}
 | 
						worker, err := api.validateSignature(r)
 | 
				
			||||||
	if r.GetJson(req) {
 | 
						if err != nil {
 | 
				
			||||||
 | 
							r.Json(GetTaskResponse{
 | 
				
			||||||
 | 
								Ok:      false,
 | 
				
			||||||
 | 
								Message: err.Error(),
 | 
				
			||||||
 | 
							}, 403)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		res := api.Database.ReleaseTask(req.TaskId, req.WorkerId, req.Success)
 | 
						var req ReleaseTaskRequest
 | 
				
			||||||
 | 
						if r.GetJson(&req) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							res := api.Database.ReleaseTask(req.TaskId, &worker.Id, req.Success)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		response := ReleaseTaskResponse{
 | 
							response := ReleaseTaskResponse{
 | 
				
			||||||
			Ok: res,
 | 
								Ok: res,
 | 
				
			||||||
 | 
				
			|||||||
@ -3,17 +3,28 @@ package api
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/Sirupsen/logrus"
 | 
						"github.com/Sirupsen/logrus"
 | 
				
			||||||
	"github.com/google/uuid"
 | 
						"github.com/google/uuid"
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
	"src/task_tracker/storage"
 | 
						"src/task_tracker/storage"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CreateWorkerRequest struct {
 | 
					type CreateWorkerRequest struct {
 | 
				
			||||||
 | 
						Alias string `json:"alias"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UpdateWorkerRequest struct {
 | 
				
			||||||
 | 
						Alias string `json:"alias"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UpdateWorkerResponse struct {
 | 
				
			||||||
 | 
						Ok      bool   `json:"ok"`
 | 
				
			||||||
 | 
						Message string `json:"message,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CreateWorkerResponse struct {
 | 
					type CreateWorkerResponse struct {
 | 
				
			||||||
	Ok      bool            `json:"ok"`
 | 
						Ok      bool            `json:"ok"`
 | 
				
			||||||
	Message string          `json:"message,omitempty"`
 | 
						Message string          `json:"message,omitempty"`
 | 
				
			||||||
	WorkerId uuid.UUID `json:"id,omitempty"`
 | 
						Worker  *storage.Worker `json:"worker,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type GetWorkerResponse struct {
 | 
					type GetWorkerResponse struct {
 | 
				
			||||||
@ -55,13 +66,13 @@ func (api *WebAPI) WorkerCreate(r *Request) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id, err := api.workerCreate(workerReq, getIdentity(r))
 | 
						worker, err := api.workerCreate(workerReq, getIdentity(r))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		handleErr(err, r)
 | 
							handleErr(err, r)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		r.OkJson(CreateWorkerResponse{
 | 
							r.OkJson(CreateWorkerResponse{
 | 
				
			||||||
			Ok:     true,
 | 
								Ok:     true,
 | 
				
			||||||
			WorkerId: id,
 | 
								Worker: worker,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -84,6 +95,9 @@ func (api *WebAPI) WorkerGet(r *Request) {
 | 
				
			|||||||
	worker := api.Database.GetWorker(id)
 | 
						worker := api.Database.GetWorker(id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if worker != nil {
 | 
						if worker != nil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							worker.Secret = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		r.OkJson(GetWorkerResponse{
 | 
							r.OkJson(GetWorkerResponse{
 | 
				
			||||||
			Ok:     true,
 | 
								Ok:     true,
 | 
				
			||||||
			Worker: worker,
 | 
								Worker: worker,
 | 
				
			||||||
@ -136,22 +150,75 @@ func (api *WebAPI) WorkerRemoveAccess(r *Request) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (api *WebAPI) workerCreate(request *CreateWorkerRequest, identity *storage.Identity) (uuid.UUID, error) {
 | 
					func (api *WebAPI) WorkerUpdate(r *Request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						worker, err := api.validateSignature(r)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							r.Json(GetTaskResponse{
 | 
				
			||||||
 | 
								Ok:      false,
 | 
				
			||||||
 | 
								Message: err.Error(),
 | 
				
			||||||
 | 
							}, 403)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req := &UpdateWorkerRequest{}
 | 
				
			||||||
 | 
						if r.GetJson(req) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							worker.Alias = req.Alias
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ok := api.Database.UpdateWorker(worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ok {
 | 
				
			||||||
 | 
								r.OkJson(UpdateWorkerResponse{
 | 
				
			||||||
 | 
									Ok: true,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								r.OkJson(UpdateWorkerResponse{
 | 
				
			||||||
 | 
									Ok:      false,
 | 
				
			||||||
 | 
									Message: "Could not update worker",
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (api *WebAPI) workerCreate(request *CreateWorkerRequest, identity *storage.Identity) (*storage.Worker, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if request.Alias == "" {
 | 
				
			||||||
 | 
							request.Alias = "default_alias"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	worker := storage.Worker{
 | 
						worker := storage.Worker{
 | 
				
			||||||
		Id:       uuid.New(),
 | 
							Id:       uuid.New(),
 | 
				
			||||||
		Created:  time.Now().Unix(),
 | 
							Created:  time.Now().Unix(),
 | 
				
			||||||
		Identity: identity,
 | 
							Identity: identity,
 | 
				
			||||||
 | 
							Secret:   makeSecret(),
 | 
				
			||||||
 | 
							Alias:    request.Alias,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	api.Database.SaveWorker(&worker)
 | 
						api.Database.SaveWorker(&worker)
 | 
				
			||||||
	return worker.Id, nil
 | 
						return &worker, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func canCreateWorker(r *Request, cwr *CreateWorkerRequest, identity *storage.Identity) bool {
 | 
					func canCreateWorker(r *Request, cwr *CreateWorkerRequest, identity *storage.Identity) bool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if cwr.Alias == "unassigned" {
 | 
				
			||||||
 | 
							//Reserved alias
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func makeSecret() []byte {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secret := make([]byte, 32)
 | 
				
			||||||
 | 
						for i := 0; i < 32; i++ {
 | 
				
			||||||
 | 
							secret[i] = byte(rand.Int31())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return secret
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getIdentity(r *Request) *storage.Identity {
 | 
					func getIdentity(r *Request) *storage.Identity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	identity := storage.Identity{
 | 
						identity := storage.Identity{
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,11 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
	"src/task_tracker/api"
 | 
						"src/task_tracker/api"
 | 
				
			||||||
	"src/task_tracker/config"
 | 
						"src/task_tracker/config"
 | 
				
			||||||
	"src/task_tracker/storage"
 | 
						"src/task_tracker/storage"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func tmpDebugSetup() {
 | 
					func tmpDebugSetup() {
 | 
				
			||||||
@ -15,6 +17,7 @@ func tmpDebugSetup() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rand.Seed(time.Now().UTC().UnixNano())
 | 
				
			||||||
	config.SetupConfig()
 | 
						config.SetupConfig()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	webApi := api.New()
 | 
						webApi := api.New()
 | 
				
			||||||
 | 
				
			|||||||
@ -26,9 +26,10 @@ CREATE TABLE worker_identity
 | 
				
			|||||||
CREATE TABLE worker
 | 
					CREATE TABLE worker
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
  id       TEXT PRIMARY KEY,
 | 
					  id       TEXT PRIMARY KEY,
 | 
				
			||||||
  alias    TEXT DEFAULT NULL,
 | 
					  alias    TEXT,
 | 
				
			||||||
  created  INTEGER,
 | 
					  created  INTEGER,
 | 
				
			||||||
  identity INTEGER REFERENCES workerIdentity (id)
 | 
					  identity INTEGER REFERENCES worker_identity (id),
 | 
				
			||||||
 | 
					  secret   BYTEA
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE project
 | 
					CREATE TABLE project
 | 
				
			||||||
@ -61,7 +62,8 @@ CREATE TABLE task
 | 
				
			|||||||
  status          Status  DEFAULT 'new',
 | 
					  status          Status  DEFAULT 'new',
 | 
				
			||||||
  recipe          TEXT,
 | 
					  recipe          TEXT,
 | 
				
			||||||
  max_assign_time INTEGER DEFAULT 0,
 | 
					  max_assign_time INTEGER DEFAULT 0,
 | 
				
			||||||
  assign_time     INTEGER DEFAULT 0
 | 
					  assign_time     INTEGER DEFAULT 0,
 | 
				
			||||||
 | 
					  hash64          BIGINT  DEFAULT NULL UNIQUE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE log_entry
 | 
					CREATE TABLE log_entry
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@ package storage
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
						"database/sql"
 | 
				
			||||||
	"github.com/Sirupsen/logrus"
 | 
						"github.com/Sirupsen/logrus"
 | 
				
			||||||
	"github.com/google/uuid"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -19,7 +18,7 @@ type Project struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type AssignedTasks struct {
 | 
					type AssignedTasks struct {
 | 
				
			||||||
	Assignee  uuid.UUID `json:"assignee"`
 | 
						Assignee  string `json:"assignee"`
 | 
				
			||||||
	TaskCount int64  `json:"task_count"`
 | 
						TaskCount int64  `json:"task_count"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -116,14 +115,16 @@ func (database *Database) GetProjectWithRepoName(repoName string) *Project {
 | 
				
			|||||||
	return project
 | 
						return project
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (database *Database) UpdateProject(project *Project) {
 | 
					func (database *Database) UpdateProject(project *Project) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.getDB()
 | 
						db := database.getDB()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res, err := db.Exec(`UPDATE project 
 | 
						res, err := db.Exec(`UPDATE project 
 | 
				
			||||||
		SET (priority, name, clone_url, git_repo, version, motd, public) = ($1,$2,$3,$4,$5,$6,$7) WHERE id=$8`,
 | 
							SET (priority, name, clone_url, git_repo, version, motd, public) = ($1,$2,$3,$4,$5,$6,$7) WHERE id=$8`,
 | 
				
			||||||
		project.Priority, project.Name, project.CloneUrl, project.GitRepo, project.Version, project.Motd, project.Public, project.Id)
 | 
							project.Priority, project.Name, project.CloneUrl, project.GitRepo, project.Version, project.Motd, project.Public, project.Id)
 | 
				
			||||||
	handleErr(err)
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rowsAffected, _ := res.RowsAffected()
 | 
						rowsAffected, _ := res.RowsAffected()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -132,7 +133,7 @@ func (database *Database) UpdateProject(project *Project) {
 | 
				
			|||||||
		"rowsAffected": rowsAffected,
 | 
							"rowsAffected": rowsAffected,
 | 
				
			||||||
	}).Trace("Database.updateProject UPDATE project")
 | 
						}).Trace("Database.updateProject UPDATE project")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (database *Database) GetProjectStats(id int64) *ProjectStats {
 | 
					func (database *Database) GetProjectStats(id int64) *ProjectStats {
 | 
				
			||||||
@ -154,18 +155,27 @@ func (database *Database) GetProjectStats(id int64) *ProjectStats {
 | 
				
			|||||||
			logrus.WithError(err).WithFields(logrus.Fields{
 | 
								logrus.WithError(err).WithFields(logrus.Fields{
 | 
				
			||||||
				"id": id,
 | 
									"id": id,
 | 
				
			||||||
			}).Trace("Get project stats: No task for this project")
 | 
								}).Trace("Get project stats: No task for this project")
 | 
				
			||||||
			return nil
 | 
					
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//todo: only expose worker alias
 | 
							rows, err := db.Query(`SELECT worker.alias, COUNT(*) as wc FROM TASK
 | 
				
			||||||
		rows, err := db.Query(`SELECT assignee, COUNT(*) FROM TASK
 | 
					  			LEFT JOIN worker ON TASK.assignee = worker.id WHERE project=$1 
 | 
				
			||||||
  			LEFT JOIN worker ON TASK.assignee = worker.id WHERE project=$1 GROUP BY assignee`, id)
 | 
								GROUP BY worker.id ORDER BY wc LIMIT 10`, id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							stats.Assignees = []*AssignedTasks{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for rows.Next() {
 | 
							for rows.Next() {
 | 
				
			||||||
			assignee := AssignedTasks{}
 | 
								assignee := AssignedTasks{}
 | 
				
			||||||
			err = rows.Scan(&assignee.Assignee, &assignee.TaskCount)
 | 
								var assigneeAlias sql.NullString
 | 
				
			||||||
 | 
								err = rows.Scan(&assigneeAlias, &assignee.TaskCount)
 | 
				
			||||||
			handleErr(err)
 | 
								handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if assigneeAlias.Valid {
 | 
				
			||||||
 | 
									assignee.Assignee = assigneeAlias.String
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									assignee.Assignee = "unassigned"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			stats.Assignees = append(stats.Assignees, &assignee)
 | 
								stats.Assignees = append(stats.Assignees, &assignee)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -182,8 +192,8 @@ func (database Database) GetAllProjectsStats() *[]ProjectStats {
 | 
				
			|||||||
       	SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) failedCount,
 | 
					       	SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) failedCount,
 | 
				
			||||||
		SUM(CASE WHEN status='closed' THEN 1 ELSE 0 END) closedCount,
 | 
							SUM(CASE WHEN status='closed' THEN 1 ELSE 0 END) closedCount,
 | 
				
			||||||
       	p.*
 | 
					       	p.*
 | 
				
			||||||
		FROM task INNER JOIN project p on task.project = p.id
 | 
							FROM task RIGHT JOIN project p on task.project = p.id
 | 
				
			||||||
		GROUP BY p.id`)
 | 
							GROUP BY p.id ORDER BY p.name`)
 | 
				
			||||||
	handleErr(err)
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for rows.Next() {
 | 
						for rows.Next() {
 | 
				
			||||||
@ -191,7 +201,7 @@ func (database Database) GetAllProjectsStats() *[]ProjectStats {
 | 
				
			|||||||
		stats := ProjectStats{}
 | 
							stats := ProjectStats{}
 | 
				
			||||||
		p := &Project{}
 | 
							p := &Project{}
 | 
				
			||||||
		err := rows.Scan(&stats.NewTaskCount, &stats.FailedTaskCount, &stats.ClosedTaskCount,
 | 
							err := rows.Scan(&stats.NewTaskCount, &stats.FailedTaskCount, &stats.ClosedTaskCount,
 | 
				
			||||||
			&p.Id, &p.Priority, &p.Motd, &p.Name, &p.CloneUrl, &p.GitRepo, &p.Version, &p.Public)
 | 
								&p.Id, &p.Priority, &p.Name, &p.CloneUrl, &p.GitRepo, &p.Version, &p.Motd, &p.Public)
 | 
				
			||||||
		handleErr(err)
 | 
							handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		stats.Project = p
 | 
							stats.Project = p
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ package storage
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
						"database/sql"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"github.com/Sirupsen/logrus"
 | 
						"github.com/Sirupsen/logrus"
 | 
				
			||||||
	"github.com/google/uuid"
 | 
						"github.com/google/uuid"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -19,13 +20,14 @@ type Task struct {
 | 
				
			|||||||
	AssignTime    int64     `json:"assign_time"`
 | 
						AssignTime    int64     `json:"assign_time"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (database *Database) SaveTask(task *Task, project int64) error {
 | 
					func (database *Database) SaveTask(task *Task, project int64, hash64 int64) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.getDB()
 | 
						db := database.getDB()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res, err := db.Exec(`
 | 
						//TODO: For some reason it refuses to insert the 64-bit value unless I do that...
 | 
				
			||||||
	INSERT INTO task (project, max_retries, recipe, priority, max_assign_time) 
 | 
						res, err := db.Exec(fmt.Sprintf(`
 | 
				
			||||||
	VALUES ($1,$2,$3,$4,$5)`,
 | 
						INSERT INTO task (project, max_retries, recipe, priority, max_assign_time, hash64) 
 | 
				
			||||||
 | 
						VALUES ($1,$2,$3,$4,$5,NULLIF(%d, 0))`, hash64),
 | 
				
			||||||
		project, task.MaxRetries, task.Recipe, task.Priority, task.MaxAssignTime)
 | 
							project, task.MaxRetries, task.Recipe, task.Priority, task.MaxAssignTime)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logrus.WithError(err).WithFields(logrus.Fields{
 | 
							logrus.WithError(err).WithFields(logrus.Fields{
 | 
				
			||||||
@ -57,7 +59,7 @@ func (database *Database) GetTask(worker *Worker) *Task {
 | 
				
			|||||||
		SELECT task.id
 | 
							SELECT task.id
 | 
				
			||||||
	FROM task
 | 
						FROM task
 | 
				
			||||||
	INNER JOIN project p on task.project = p.id
 | 
						INNER JOIN project p on task.project = p.id
 | 
				
			||||||
	WHERE assignee IS NULL
 | 
						WHERE assignee IS NULL AND task.status='new'
 | 
				
			||||||
		AND (p.public OR EXISTS (
 | 
							AND (p.public OR EXISTS (
 | 
				
			||||||
		  SELECT 1 FROM worker_has_access_to_project a WHERE a.worker=$1 AND a.project=p.id
 | 
							  SELECT 1 FROM worker_has_access_to_project a WHERE a.worker=$1 AND a.project=p.id
 | 
				
			||||||
		))
 | 
							))
 | 
				
			||||||
@ -88,7 +90,8 @@ func (database *Database) GetTask(worker *Worker) *Task {
 | 
				
			|||||||
func getTaskById(id int64, db *sql.DB) *Task {
 | 
					func getTaskById(id int64, db *sql.DB) *Task {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	row := db.QueryRow(`
 | 
						row := db.QueryRow(`
 | 
				
			||||||
	SELECT * FROM task 
 | 
						SELECT task.id, task.priority, task.project, assignee, retries, max_retries,
 | 
				
			||||||
 | 
						        status, recipe, max_assign_time, assign_time, project.* FROM task 
 | 
				
			||||||
	  INNER JOIN project ON task.project = project.id
 | 
						  INNER JOIN project ON task.project = project.id
 | 
				
			||||||
	WHERE task.id=$1`, id)
 | 
						WHERE task.id=$1`, id)
 | 
				
			||||||
	task := scanTask(row)
 | 
						task := scanTask(row)
 | 
				
			||||||
@ -109,11 +112,11 @@ func (database Database) ReleaseTask(id int64, workerId *uuid.UUID, success bool
 | 
				
			|||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if success {
 | 
						if success {
 | 
				
			||||||
		res, err = db.Exec(`UPDATE task SET (status, assignee) = ('closed', NULL)
 | 
							res, err = db.Exec(`UPDATE task SET (status, assignee) = ('closed', NULL)
 | 
				
			||||||
		WHERE id=$2 AND task.assignee=$2`, id, workerId)
 | 
							WHERE id=$1 AND task.assignee=$2`, id, workerId)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		res, err = db.Exec(`UPDATE task SET (status, assignee, retries) = 
 | 
							res, err = db.Exec(`UPDATE task SET (status, assignee, retries) = 
 | 
				
			||||||
  		(CASE WHEN retries+1 >= max_retries THEN 'failed' ELSE 'new' END, NULL, retries+1)
 | 
					  		(CASE WHEN retries+1 >= max_retries THEN 'failed' ELSE 'new' END, NULL, retries+1)
 | 
				
			||||||
		WHERE id=$2 AND assignee=$2`, id, workerId)
 | 
							WHERE id=$1 AND assignee=$2`, id, workerId)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	handleErr(err)
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -138,7 +141,7 @@ func (database *Database) GetTaskFromProject(worker *Worker, projectId int64) *T
 | 
				
			|||||||
		SELECT task.id
 | 
							SELECT task.id
 | 
				
			||||||
	FROM task
 | 
						FROM task
 | 
				
			||||||
	INNER JOIN project p on task.project = p.id
 | 
						INNER JOIN project p on task.project = p.id
 | 
				
			||||||
	WHERE assignee IS NULL AND p.id=$2
 | 
						WHERE assignee IS NULL AND p.id=$2 AND status='new'
 | 
				
			||||||
		AND (p.public OR EXISTS (
 | 
							AND (p.public OR EXISTS (
 | 
				
			||||||
		  SELECT 1 FROM worker_has_access_to_project a WHERE a.worker=$1 AND a.project=$2
 | 
							  SELECT 1 FROM worker_has_access_to_project a WHERE a.worker=$1 AND a.project=$2
 | 
				
			||||||
		))
 | 
							))
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,8 @@ type Worker struct {
 | 
				
			|||||||
	Id       uuid.UUID `json:"id"`
 | 
						Id       uuid.UUID `json:"id"`
 | 
				
			||||||
	Created  int64     `json:"created"`
 | 
						Created  int64     `json:"created"`
 | 
				
			||||||
	Identity *Identity `json:"identity"`
 | 
						Identity *Identity `json:"identity"`
 | 
				
			||||||
 | 
						Alias    string    `json:"alias,omitempty"`
 | 
				
			||||||
 | 
						Secret   []byte    `json:"secret"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (database *Database) SaveWorker(worker *Worker) {
 | 
					func (database *Database) SaveWorker(worker *Worker) {
 | 
				
			||||||
@ -24,8 +26,8 @@ func (database *Database) SaveWorker(worker *Worker) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	identityId := getOrCreateIdentity(worker.Identity, db)
 | 
						identityId := getOrCreateIdentity(worker.Identity, db)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res, err := db.Exec("INSERT INTO worker (id, created, identity) VALUES ($1,$2,$3)",
 | 
						res, err := db.Exec("INSERT INTO worker (id, created, identity, secret, alias) VALUES ($1,$2,$3,$4,$5)",
 | 
				
			||||||
		worker.Id, worker.Created, identityId)
 | 
							worker.Id, worker.Created, identityId, worker.Secret, worker.Alias)
 | 
				
			||||||
	handleErr(err)
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var rowsAffected, _ = res.RowsAffected()
 | 
						var rowsAffected, _ = res.RowsAffected()
 | 
				
			||||||
@ -41,8 +43,8 @@ func (database *Database) GetWorker(id uuid.UUID) *Worker {
 | 
				
			|||||||
	worker := &Worker{}
 | 
						worker := &Worker{}
 | 
				
			||||||
	var identityId int64
 | 
						var identityId int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	row := db.QueryRow("SELECT id, created, identity FROM worker WHERE id=$1", id)
 | 
						row := db.QueryRow("SELECT id, created, identity, secret, alias FROM worker WHERE id=$1", id)
 | 
				
			||||||
	err := row.Scan(&worker.Id, &worker.Created, &identityId)
 | 
						err := row.Scan(&worker.Id, &worker.Created, &identityId, &worker.Secret, &worker.Alias)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logrus.WithFields(logrus.Fields{
 | 
							logrus.WithFields(logrus.Fields{
 | 
				
			||||||
			"id": id,
 | 
								"id": id,
 | 
				
			||||||
@ -64,7 +66,7 @@ func getIdentity(id int64, db *sql.DB) (*Identity, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	identity := &Identity{}
 | 
						identity := &Identity{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	row := db.QueryRow("SELECT remote_addr, user_agent FROM workeridentity WHERE id=$1", id)
 | 
						row := db.QueryRow("SELECT remote_addr, user_agent FROM worker_identity WHERE id=$1", id)
 | 
				
			||||||
	err := row.Scan(&identity.RemoteAddr, &identity.UserAgent)
 | 
						err := row.Scan(&identity.RemoteAddr, &identity.UserAgent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -80,7 +82,7 @@ func getIdentity(id int64, db *sql.DB) (*Identity, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func getOrCreateIdentity(identity *Identity, db *sql.DB) int64 {
 | 
					func getOrCreateIdentity(identity *Identity, db *sql.DB) int64 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res, err := db.Exec("INSERT INTO workeridentity (remote_addr, user_agent) VALUES ($1,$2) ON CONFLICT DO NOTHING",
 | 
						res, err := db.Exec("INSERT INTO worker_identity (remote_addr, user_agent) VALUES ($1,$2) ON CONFLICT DO NOTHING",
 | 
				
			||||||
		identity.RemoteAddr, identity.UserAgent)
 | 
							identity.RemoteAddr, identity.UserAgent)
 | 
				
			||||||
	handleErr(err)
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -89,7 +91,7 @@ func getOrCreateIdentity(identity *Identity, db *sql.DB) int64 {
 | 
				
			|||||||
		"rowsAffected": rowsAffected,
 | 
							"rowsAffected": rowsAffected,
 | 
				
			||||||
	}).Trace("Database.saveWorker INSERT workerIdentity")
 | 
						}).Trace("Database.saveWorker INSERT workerIdentity")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	row := db.QueryRow("SELECT (id) FROM workeridentity WHERE remote_addr=$1", identity.RemoteAddr)
 | 
						row := db.QueryRow("SELECT (id) FROM worker_identity WHERE remote_addr=$1", identity.RemoteAddr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var rowId int64
 | 
						var rowId int64
 | 
				
			||||||
	err = row.Scan(&rowId)
 | 
						err = row.Scan(&rowId)
 | 
				
			||||||
@ -127,7 +129,7 @@ func (database *Database) GrantAccess(workerId *uuid.UUID, projectId int64) bool
 | 
				
			|||||||
	return rowsAffected == 1
 | 
						return rowsAffected == 1
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (database Database) RemoveAccess(workerId *uuid.UUID, projectId int64) bool {
 | 
					func (database *Database) RemoveAccess(workerId *uuid.UUID, projectId int64) bool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.getDB()
 | 
						db := database.getDB()
 | 
				
			||||||
	res, err := db.Exec(`DELETE FROM worker_has_access_to_project WHERE worker=$1 AND project=$2`,
 | 
						res, err := db.Exec(`DELETE FROM worker_has_access_to_project WHERE worker=$1 AND project=$2`,
 | 
				
			||||||
@ -144,3 +146,20 @@ func (database Database) RemoveAccess(workerId *uuid.UUID, projectId int64) bool
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return rowsAffected == 1
 | 
						return rowsAffected == 1
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (database *Database) UpdateWorker(worker *Worker) bool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db := database.getDB()
 | 
				
			||||||
 | 
						res, err := db.Exec(`UPDATE worker SET alias=$1 WHERE id=$2`,
 | 
				
			||||||
 | 
							worker.Alias, worker.Id)
 | 
				
			||||||
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rowsAffected, _ := res.RowsAffected()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logrus.WithFields(logrus.Fields{
 | 
				
			||||||
 | 
							"rowsAffected": rowsAffected,
 | 
				
			||||||
 | 
							"worker":       worker,
 | 
				
			||||||
 | 
						}).Trace("Database.UpdateWorker UPDATE worker")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rowsAffected == 1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestWebHookNoSignature(t *testing.T) {
 | 
					func TestWebHookNoSignature(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := Post("/git/receivehook", api.GitPayload{})
 | 
						r := Post("/git/receivehook", api.GitPayload{}, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if r.StatusCode != 403 {
 | 
						if r.StatusCode != 403 {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestIndex(t *testing.T) {
 | 
					func TestIndex(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := Get("/")
 | 
						r := Get("/", nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	body, _ := ioutil.ReadAll(r.Body)
 | 
						body, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
	var info api.Info
 | 
						var info api.Info
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,7 @@ func TestTraceValid(t *testing.T) {
 | 
				
			|||||||
		Scope:     "test",
 | 
							Scope:     "test",
 | 
				
			||||||
		Message:   "This is a test message",
 | 
							Message:   "This is a test message",
 | 
				
			||||||
		TimeStamp: time.Now().Unix(),
 | 
							TimeStamp: time.Now().Unix(),
 | 
				
			||||||
	})
 | 
						}, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if r.StatusCode != 200 {
 | 
						if r.StatusCode != 200 {
 | 
				
			||||||
		t.Fail()
 | 
							t.Fail()
 | 
				
			||||||
@ -27,7 +27,7 @@ func TestTraceInvalidScope(t *testing.T) {
 | 
				
			|||||||
	r := Post("/log/trace", api.LogRequest{
 | 
						r := Post("/log/trace", api.LogRequest{
 | 
				
			||||||
		Message:   "this is a test message",
 | 
							Message:   "this is a test message",
 | 
				
			||||||
		TimeStamp: time.Now().Unix(),
 | 
							TimeStamp: time.Now().Unix(),
 | 
				
			||||||
	})
 | 
						}, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if r.StatusCode != 500 {
 | 
						if r.StatusCode != 500 {
 | 
				
			||||||
		t.Fail()
 | 
							t.Fail()
 | 
				
			||||||
@ -37,7 +37,7 @@ func TestTraceInvalidScope(t *testing.T) {
 | 
				
			|||||||
		Scope:     "",
 | 
							Scope:     "",
 | 
				
			||||||
		Message:   "this is a test message",
 | 
							Message:   "this is a test message",
 | 
				
			||||||
		TimeStamp: time.Now().Unix(),
 | 
							TimeStamp: time.Now().Unix(),
 | 
				
			||||||
	})
 | 
						}, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if r.StatusCode != 500 {
 | 
						if r.StatusCode != 500 {
 | 
				
			||||||
		t.Fail()
 | 
							t.Fail()
 | 
				
			||||||
@ -52,7 +52,7 @@ func TestTraceInvalidMessage(t *testing.T) {
 | 
				
			|||||||
		Scope:     "test",
 | 
							Scope:     "test",
 | 
				
			||||||
		Message:   "",
 | 
							Message:   "",
 | 
				
			||||||
		TimeStamp: time.Now().Unix(),
 | 
							TimeStamp: time.Now().Unix(),
 | 
				
			||||||
	})
 | 
						}, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if r.StatusCode != 500 {
 | 
						if r.StatusCode != 500 {
 | 
				
			||||||
		t.Fail()
 | 
							t.Fail()
 | 
				
			||||||
@ -66,7 +66,7 @@ func TestTraceInvalidTime(t *testing.T) {
 | 
				
			|||||||
	r := Post("/log/trace", api.LogRequest{
 | 
						r := Post("/log/trace", api.LogRequest{
 | 
				
			||||||
		Scope:   "test",
 | 
							Scope:   "test",
 | 
				
			||||||
		Message: "test",
 | 
							Message: "test",
 | 
				
			||||||
	})
 | 
						}, nil)
 | 
				
			||||||
	if r.StatusCode != 500 {
 | 
						if r.StatusCode != 500 {
 | 
				
			||||||
		t.Fail()
 | 
							t.Fail()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -81,7 +81,7 @@ func TestWarnValid(t *testing.T) {
 | 
				
			|||||||
		Scope:     "test",
 | 
							Scope:     "test",
 | 
				
			||||||
		Message:   "test",
 | 
							Message:   "test",
 | 
				
			||||||
		TimeStamp: time.Now().Unix(),
 | 
							TimeStamp: time.Now().Unix(),
 | 
				
			||||||
	})
 | 
						}, nil)
 | 
				
			||||||
	if r.StatusCode != 200 {
 | 
						if r.StatusCode != 200 {
 | 
				
			||||||
		t.Fail()
 | 
							t.Fail()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -93,7 +93,7 @@ func TestInfoValid(t *testing.T) {
 | 
				
			|||||||
		Scope:     "test",
 | 
							Scope:     "test",
 | 
				
			||||||
		Message:   "test",
 | 
							Message:   "test",
 | 
				
			||||||
		TimeStamp: time.Now().Unix(),
 | 
							TimeStamp: time.Now().Unix(),
 | 
				
			||||||
	})
 | 
						}, nil)
 | 
				
			||||||
	if r.StatusCode != 200 {
 | 
						if r.StatusCode != 200 {
 | 
				
			||||||
		t.Fail()
 | 
							t.Fail()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -105,7 +105,7 @@ func TestErrorValid(t *testing.T) {
 | 
				
			|||||||
		Scope:     "test",
 | 
							Scope:     "test",
 | 
				
			||||||
		Message:   "test",
 | 
							Message:   "test",
 | 
				
			||||||
		TimeStamp: time.Now().Unix(),
 | 
							TimeStamp: time.Now().Unix(),
 | 
				
			||||||
	})
 | 
						}, nil)
 | 
				
			||||||
	if r.StatusCode != 200 {
 | 
						if r.StatusCode != 200 {
 | 
				
			||||||
		t.Fail()
 | 
							t.Fail()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -171,7 +171,7 @@ func getLogs(since int64, level logrus.Level) *api.GetLogResponse {
 | 
				
			|||||||
	r := Post(fmt.Sprintf("/logs"), api.GetLogRequest{
 | 
						r := Post(fmt.Sprintf("/logs"), api.GetLogRequest{
 | 
				
			||||||
		Since: since,
 | 
							Since: since,
 | 
				
			||||||
		Level: level,
 | 
							Level: level,
 | 
				
			||||||
	})
 | 
						}, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := &api.GetLogResponse{}
 | 
						resp := &api.GetLogResponse{}
 | 
				
			||||||
	data, _ := ioutil.ReadAll(r.Body)
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@ package test
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/google/uuid"
 | 
					 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"src/task_tracker/api"
 | 
						"src/task_tracker/api"
 | 
				
			||||||
@ -132,28 +131,30 @@ func TestGetProjectStats(t *testing.T) {
 | 
				
			|||||||
		CloneUrl: "http://github.com/drone/test",
 | 
							CloneUrl: "http://github.com/drone/test",
 | 
				
			||||||
		GitRepo:  "drone/test",
 | 
							GitRepo:  "drone/test",
 | 
				
			||||||
		Priority: 3,
 | 
							Priority: 3,
 | 
				
			||||||
 | 
							Public:   true,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pid := r.Id
 | 
						pid := r.Id
 | 
				
			||||||
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	createTask(api.CreateTaskRequest{
 | 
						createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Priority:   1,
 | 
							Priority:   1,
 | 
				
			||||||
		Project:    pid,
 | 
							Project:    pid,
 | 
				
			||||||
		MaxRetries: 0,
 | 
							MaxRetries: 0,
 | 
				
			||||||
		Recipe:     "{}",
 | 
							Recipe:     "{}",
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
	createTask(api.CreateTaskRequest{
 | 
						createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Priority:   2,
 | 
							Priority:   2,
 | 
				
			||||||
		Project:    pid,
 | 
							Project:    pid,
 | 
				
			||||||
		MaxRetries: 0,
 | 
							MaxRetries: 0,
 | 
				
			||||||
		Recipe:     "{}",
 | 
							Recipe:     "{}",
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
	createTask(api.CreateTaskRequest{
 | 
						createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Priority:   3,
 | 
							Priority:   3,
 | 
				
			||||||
		Project:    pid,
 | 
							Project:    pid,
 | 
				
			||||||
		MaxRetries: 0,
 | 
							MaxRetries: 0,
 | 
				
			||||||
		Recipe:     "{}",
 | 
							Recipe:     "{}",
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stats := getProjectStats(pid)
 | 
						stats := getProjectStats(pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -169,7 +170,7 @@ func TestGetProjectStats(t *testing.T) {
 | 
				
			|||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if stats.Stats.Assignees[0].Assignee != uuid.Nil {
 | 
						if stats.Stats.Assignees[0].Assignee != "unassigned" {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if stats.Stats.Assignees[0].TaskCount != 3 {
 | 
						if stats.Stats.Assignees[0].TaskCount != 3 {
 | 
				
			||||||
@ -189,19 +190,132 @@ func TestGetProjectStatsNotFound(t *testing.T) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
	s := getProjectStats(r.Id)
 | 
						s := getProjectStats(r.Id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if s.Ok != false {
 | 
						if s.Ok != true {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(s.Message) <= 0 {
 | 
						if s.Stats == nil {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUpdateProjectValid(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pid := createProject(api.CreateProjectRequest{
 | 
				
			||||||
 | 
							Public:   true,
 | 
				
			||||||
 | 
							Version:  "versionA",
 | 
				
			||||||
 | 
							Motd:     "MotdA",
 | 
				
			||||||
 | 
							Name:     "NameA",
 | 
				
			||||||
 | 
							CloneUrl: "CloneUrlA",
 | 
				
			||||||
 | 
							GitRepo:  "GitRepoA",
 | 
				
			||||||
 | 
							Priority: 1,
 | 
				
			||||||
 | 
						}).Id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						updateResp := updateProject(api.UpdateProjectRequest{
 | 
				
			||||||
 | 
							Priority: 2,
 | 
				
			||||||
 | 
							GitRepo:  "GitRepoB",
 | 
				
			||||||
 | 
							CloneUrl: "CloneUrlB",
 | 
				
			||||||
 | 
							Name:     "NameB",
 | 
				
			||||||
 | 
							Motd:     "MotdB",
 | 
				
			||||||
 | 
							Public:   false,
 | 
				
			||||||
 | 
						}, pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if updateResp.Ok != true {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						proj, _ := getProject(pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if proj.Project.Public != false {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if proj.Project.Motd != "MotdB" {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if proj.Project.CloneUrl != "CloneUrlB" {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if proj.Project.GitRepo != "GitRepoB" {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if proj.Project.Priority != 2 {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUpdateProjectInvalid(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pid := createProject(api.CreateProjectRequest{
 | 
				
			||||||
 | 
							Public:   true,
 | 
				
			||||||
 | 
							Version:  "lllllllllllll",
 | 
				
			||||||
 | 
							Motd:     "2wwwwwwwwwwwwwww",
 | 
				
			||||||
 | 
							Name:     "aaaaaaaaaaaaaaaaaaaaaa",
 | 
				
			||||||
 | 
							CloneUrl: "333333333333333",
 | 
				
			||||||
 | 
							GitRepo:  "llllllllllllllllllls",
 | 
				
			||||||
 | 
							Priority: 1,
 | 
				
			||||||
 | 
						}).Id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						updateResp := updateProject(api.UpdateProjectRequest{
 | 
				
			||||||
 | 
							Priority: -1,
 | 
				
			||||||
 | 
							GitRepo:  "GitRepo------",
 | 
				
			||||||
 | 
							CloneUrl: "CloneUrlB000000",
 | 
				
			||||||
 | 
							Name:     "NameB-0",
 | 
				
			||||||
 | 
							Motd:     "MotdB000000",
 | 
				
			||||||
 | 
							Public:   false,
 | 
				
			||||||
 | 
						}, pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if updateResp.Ok != false {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(updateResp.Message) <= 0 {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUpdateProjectConstraintFail(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pid := createProject(api.CreateProjectRequest{
 | 
				
			||||||
 | 
							Public:   true,
 | 
				
			||||||
 | 
							Version:  "testUpdateProjectConstraintFail",
 | 
				
			||||||
 | 
							Motd:     "testUpdateProjectConstraintFail",
 | 
				
			||||||
 | 
							Name:     "testUpdateProjectConstraintFail",
 | 
				
			||||||
 | 
							CloneUrl: "testUpdateProjectConstraintFail",
 | 
				
			||||||
 | 
							GitRepo:  "testUpdateProjectConstraintFail",
 | 
				
			||||||
 | 
							Priority: 1,
 | 
				
			||||||
 | 
						}).Id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						createProject(api.CreateProjectRequest{
 | 
				
			||||||
 | 
							Public:   true,
 | 
				
			||||||
 | 
							Version:  "testUpdateProjectConstraintFail_d",
 | 
				
			||||||
 | 
							Motd:     "testUpdateProjectConstraintFail_d",
 | 
				
			||||||
 | 
							Name:     "testUpdateProjectConstraintFail_d",
 | 
				
			||||||
 | 
							CloneUrl: "testUpdateProjectConstraintFail_d",
 | 
				
			||||||
 | 
							GitRepo:  "testUpdateProjectConstraintFail_d",
 | 
				
			||||||
 | 
							Priority: 1,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						updateResp := updateProject(api.UpdateProjectRequest{
 | 
				
			||||||
 | 
							Priority: 1,
 | 
				
			||||||
 | 
							GitRepo:  "testUpdateProjectConstraintFail_d",
 | 
				
			||||||
 | 
							CloneUrl: "testUpdateProjectConstraintFail_d",
 | 
				
			||||||
 | 
							Name:     "testUpdateProjectConstraintFail_d",
 | 
				
			||||||
 | 
							Motd:     "testUpdateProjectConstraintFail_d",
 | 
				
			||||||
 | 
						}, pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if updateResp.Ok != false {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(updateResp.Message) <= 0 {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func createProject(req api.CreateProjectRequest) *api.CreateProjectResponse {
 | 
					func createProject(req api.CreateProjectRequest) *api.CreateProjectResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := Post("/project/create", req)
 | 
						r := Post("/project/create", req, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var resp api.CreateProjectResponse
 | 
						var resp api.CreateProjectResponse
 | 
				
			||||||
	data, _ := ioutil.ReadAll(r.Body)
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
@ -213,7 +327,7 @@ func createProject(req api.CreateProjectRequest) *api.CreateProjectResponse {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func getProject(id int64) (*api.GetProjectResponse, *http.Response) {
 | 
					func getProject(id int64) (*api.GetProjectResponse, *http.Response) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := Get(fmt.Sprintf("/project/get/%d", id))
 | 
						r := Get(fmt.Sprintf("/project/get/%d", id), nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var getResp api.GetProjectResponse
 | 
						var getResp api.GetProjectResponse
 | 
				
			||||||
	data, _ := ioutil.ReadAll(r.Body)
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
@ -225,7 +339,7 @@ func getProject(id int64) (*api.GetProjectResponse, *http.Response) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func getProjectStats(id int64) *api.GetProjectStatsResponse {
 | 
					func getProjectStats(id int64) *api.GetProjectStatsResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := Get(fmt.Sprintf("/project/stats/%d", id))
 | 
						r := Get(fmt.Sprintf("/project/stats/%d", id), nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var getResp api.GetProjectStatsResponse
 | 
						var getResp api.GetProjectStatsResponse
 | 
				
			||||||
	data, _ := ioutil.ReadAll(r.Body)
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
@ -234,3 +348,15 @@ func getProjectStats(id int64) *api.GetProjectStatsResponse {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return &getResp
 | 
						return &getResp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func updateProject(request api.UpdateProjectRequest, pid int64) *api.UpdateProjectResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r := Post(fmt.Sprintf("/project/update/%d", pid), request, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var resp api.UpdateProjectResponse
 | 
				
			||||||
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
 | 
						err := json.Unmarshal(data, &resp)
 | 
				
			||||||
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &resp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,8 @@ func BenchmarkCreateTask(b *testing.B) {
 | 
				
			|||||||
		CloneUrl: "http://localhost",
 | 
							CloneUrl: "http://localhost",
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b.ResetTimer()
 | 
						b.ResetTimer()
 | 
				
			||||||
	for i := 0; i < b.N; i++ {
 | 
						for i := 0; i < b.N; i++ {
 | 
				
			||||||
		createTask(api.CreateTaskRequest{
 | 
							createTask(api.CreateTaskRequest{
 | 
				
			||||||
@ -22,6 +24,6 @@ func BenchmarkCreateTask(b *testing.B) {
 | 
				
			|||||||
			Priority:   1,
 | 
								Priority:   1,
 | 
				
			||||||
			Recipe:     "{}",
 | 
								Recipe:     "{}",
 | 
				
			||||||
			MaxRetries: 1,
 | 
								MaxRetries: 1,
 | 
				
			||||||
		})
 | 
							}, worker)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ import (
 | 
				
			|||||||
	"github.com/google/uuid"
 | 
						"github.com/google/uuid"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"src/task_tracker/api"
 | 
						"src/task_tracker/api"
 | 
				
			||||||
 | 
						"src/task_tracker/storage"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -18,11 +19,13 @@ func TestCreateTaskValid(t *testing.T) {
 | 
				
			|||||||
		CloneUrl: "http://github.com/test/test",
 | 
							CloneUrl: "http://github.com/test/test",
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := createTask(api.CreateTaskRequest{
 | 
						resp := createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Project:    1,
 | 
							Project:    1,
 | 
				
			||||||
		Recipe:     "{}",
 | 
							Recipe:     "{}",
 | 
				
			||||||
		MaxRetries: 3,
 | 
							MaxRetries: 3,
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if resp.Ok != true {
 | 
						if resp.Ok != true {
 | 
				
			||||||
		t.Fail()
 | 
							t.Fail()
 | 
				
			||||||
@ -31,11 +34,13 @@ func TestCreateTaskValid(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestCreateTaskInvalidProject(t *testing.T) {
 | 
					func TestCreateTaskInvalidProject(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := createTask(api.CreateTaskRequest{
 | 
						resp := createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Project:    123456,
 | 
							Project:    123456,
 | 
				
			||||||
		Recipe:     "{}",
 | 
							Recipe:     "{}",
 | 
				
			||||||
		MaxRetries: 3,
 | 
							MaxRetries: 3,
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if resp.Ok != false {
 | 
						if resp.Ok != false {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
@ -62,7 +67,9 @@ func TestGetTaskInvalidWid(t *testing.T) {
 | 
				
			|||||||
func TestGetTaskInvalidWorker(t *testing.T) {
 | 
					func TestGetTaskInvalidWorker(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id := uuid.New()
 | 
						id := uuid.New()
 | 
				
			||||||
	resp := getTask(&id)
 | 
						resp := getTask(&storage.Worker{
 | 
				
			||||||
 | 
							Id: id,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if resp.Ok != false {
 | 
						if resp.Ok != false {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
@ -76,7 +83,9 @@ func TestGetTaskInvalidWorker(t *testing.T) {
 | 
				
			|||||||
func TestGetTaskFromProjectInvalidWorker(t *testing.T) {
 | 
					func TestGetTaskFromProjectInvalidWorker(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id := uuid.New()
 | 
						id := uuid.New()
 | 
				
			||||||
	resp := getTaskFromProject(1, &id)
 | 
						resp := getTaskFromProject(1, &storage.Worker{
 | 
				
			||||||
 | 
							Id: id,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if resp.Ok != false {
 | 
						if resp.Ok != false {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
@ -89,10 +98,12 @@ func TestGetTaskFromProjectInvalidWorker(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestCreateTaskInvalidRetries(t *testing.T) {
 | 
					func TestCreateTaskInvalidRetries(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := createTask(api.CreateTaskRequest{
 | 
						resp := createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Project:    1,
 | 
							Project:    1,
 | 
				
			||||||
		MaxRetries: -1,
 | 
							MaxRetries: -1,
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if resp.Ok != false {
 | 
						if resp.Ok != false {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
@ -105,11 +116,13 @@ func TestCreateTaskInvalidRetries(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestCreateTaskInvalidRecipe(t *testing.T) {
 | 
					func TestCreateTaskInvalidRecipe(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := createTask(api.CreateTaskRequest{
 | 
						resp := createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Project:    1,
 | 
							Project:    1,
 | 
				
			||||||
		Recipe:     "",
 | 
							Recipe:     "",
 | 
				
			||||||
		MaxRetries: 3,
 | 
							MaxRetries: 3,
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if resp.Ok != false {
 | 
						if resp.Ok != false {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
@ -132,12 +145,14 @@ func TestCreateGetTask(t *testing.T) {
 | 
				
			|||||||
		Public:   true,
 | 
							Public:   true,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	createTask(api.CreateTaskRequest{
 | 
						createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Project:    resp.Id,
 | 
							Project:    resp.Id,
 | 
				
			||||||
		Recipe:     "{\"url\":\"test\"}",
 | 
							Recipe:     "{\"url\":\"test\"}",
 | 
				
			||||||
		MaxRetries: 3,
 | 
							MaxRetries: 3,
 | 
				
			||||||
		Priority:   9999,
 | 
							Priority:   9999,
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	taskResp := getTaskFromProject(resp.Id, genWid())
 | 
						taskResp := getTaskFromProject(resp.Id, genWid())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -194,26 +209,27 @@ func createTasks(prefix string) (int64, int64) {
 | 
				
			|||||||
		Priority: 999,
 | 
							Priority: 999,
 | 
				
			||||||
		Public:   true,
 | 
							Public:   true,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
						worker := genWid()
 | 
				
			||||||
	createTask(api.CreateTaskRequest{
 | 
						createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Project:  lowP.Id,
 | 
							Project:  lowP.Id,
 | 
				
			||||||
		Recipe:   "low1",
 | 
							Recipe:   "low1",
 | 
				
			||||||
		Priority: 0,
 | 
							Priority: 0,
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
	createTask(api.CreateTaskRequest{
 | 
						createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Project:  lowP.Id,
 | 
							Project:  lowP.Id,
 | 
				
			||||||
		Recipe:   "low2",
 | 
							Recipe:   "low2",
 | 
				
			||||||
		Priority: 1,
 | 
							Priority: 1,
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
	createTask(api.CreateTaskRequest{
 | 
						createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Project:  highP.Id,
 | 
							Project:  highP.Id,
 | 
				
			||||||
		Recipe:   "high1",
 | 
							Recipe:   "high1",
 | 
				
			||||||
		Priority: 100,
 | 
							Priority: 100,
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
	createTask(api.CreateTaskRequest{
 | 
						createTask(api.CreateTaskRequest{
 | 
				
			||||||
		Project:  highP.Id,
 | 
							Project:  highP.Id,
 | 
				
			||||||
		Recipe:   "high2",
 | 
							Recipe:   "high2",
 | 
				
			||||||
		Priority: 101,
 | 
							Priority: 101,
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return lowP.Id, highP.Id
 | 
						return lowP.Id, highP.Id
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -274,7 +290,7 @@ func TestTaskPriority(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestTaskNoAccess(t *testing.T) {
 | 
					func TestTaskNoAccess(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wid := genWid()
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pid := createProject(api.CreateProjectRequest{
 | 
						pid := createProject(api.CreateProjectRequest{
 | 
				
			||||||
		Name:     "This is a private proj",
 | 
							Name:     "This is a private proj",
 | 
				
			||||||
@ -292,16 +308,16 @@ func TestTaskNoAccess(t *testing.T) {
 | 
				
			|||||||
		MaxAssignTime: 10,
 | 
							MaxAssignTime: 10,
 | 
				
			||||||
		MaxRetries:    2,
 | 
							MaxRetries:    2,
 | 
				
			||||||
		Recipe:        "---",
 | 
							Recipe:        "---",
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if createResp.Ok != true {
 | 
						if createResp.Ok != true {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	grantAccess(wid, pid)
 | 
						grantAccess(&worker.Id, pid)
 | 
				
			||||||
	removeAccess(wid, pid)
 | 
						removeAccess(&worker.Id, pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tResp := getTaskFromProject(pid, wid)
 | 
						tResp := getTaskFromProject(pid, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if tResp.Ok != false {
 | 
						if tResp.Ok != false {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
@ -316,7 +332,7 @@ func TestTaskNoAccess(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestTaskHasAccess(t *testing.T) {
 | 
					func TestTaskHasAccess(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wid := genWid()
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pid := createProject(api.CreateProjectRequest{
 | 
						pid := createProject(api.CreateProjectRequest{
 | 
				
			||||||
		Name:     "This is a private proj1",
 | 
							Name:     "This is a private proj1",
 | 
				
			||||||
@ -334,15 +350,15 @@ func TestTaskHasAccess(t *testing.T) {
 | 
				
			|||||||
		MaxAssignTime: 10,
 | 
							MaxAssignTime: 10,
 | 
				
			||||||
		MaxRetries:    2,
 | 
							MaxRetries:    2,
 | 
				
			||||||
		Recipe:        "---",
 | 
							Recipe:        "---",
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if createResp.Ok != true {
 | 
						if createResp.Ok != true {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	grantAccess(wid, pid)
 | 
						grantAccess(&worker.Id, pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tResp := getTaskFromProject(pid, wid)
 | 
						tResp := getTaskFromProject(pid, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if tResp.Ok != true {
 | 
						if tResp.Ok != true {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
@ -354,16 +370,16 @@ func TestTaskHasAccess(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestNoMoreTasks(t *testing.T) {
 | 
					func TestNoMoreTasks(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wid := genWid()
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := 0; i < 15; i++ {
 | 
						for i := 0; i < 15; i++ {
 | 
				
			||||||
		getTask(wid)
 | 
							getTask(worker)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestReleaseTaskSuccess(t *testing.T) {
 | 
					func TestReleaseTaskSuccess(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//wid := genWid()
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pid := createProject(api.CreateProjectRequest{
 | 
						pid := createProject(api.CreateProjectRequest{
 | 
				
			||||||
		Priority: 0,
 | 
							Priority: 0,
 | 
				
			||||||
@ -372,6 +388,7 @@ func TestReleaseTaskSuccess(t *testing.T) {
 | 
				
			|||||||
		Version:  "11111111111111111",
 | 
							Version:  "11111111111111111",
 | 
				
			||||||
		Name:     "testreleasetask",
 | 
							Name:     "testreleasetask",
 | 
				
			||||||
		Motd:     "",
 | 
							Motd:     "",
 | 
				
			||||||
 | 
							Public:   true,
 | 
				
			||||||
	}).Id
 | 
						}).Id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	createTask(api.CreateTaskRequest{
 | 
						createTask(api.CreateTaskRequest{
 | 
				
			||||||
@ -379,13 +396,119 @@ func TestReleaseTaskSuccess(t *testing.T) {
 | 
				
			|||||||
		Project:    pid,
 | 
							Project:    pid,
 | 
				
			||||||
		Recipe:     "{}",
 | 
							Recipe:     "{}",
 | 
				
			||||||
		MaxRetries: 3,
 | 
							MaxRetries: 3,
 | 
				
			||||||
	})
 | 
						}, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						task := getTaskFromProject(pid, worker).Task
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						releaseResp := releaseTask(api.ReleaseTaskRequest{
 | 
				
			||||||
 | 
							TaskId:  task.Id,
 | 
				
			||||||
 | 
							Success: true,
 | 
				
			||||||
 | 
						}, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if releaseResp.Ok != true {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						otherTask := getTaskFromProject(pid, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Shouldn't have more tasks available
 | 
				
			||||||
 | 
						if otherTask.Ok != false {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func createTask(request api.CreateTaskRequest) *api.CreateTaskResponse {
 | 
					func TestCreateIntCollision(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := Post("/task/create", request)
 | 
						pid := createProject(api.CreateProjectRequest{
 | 
				
			||||||
 | 
							Priority: 1,
 | 
				
			||||||
 | 
							GitRepo:  "testcreateintcollision",
 | 
				
			||||||
 | 
							CloneUrl: "testcreateintcollision",
 | 
				
			||||||
 | 
							Motd:     "testcreateintcollision",
 | 
				
			||||||
 | 
							Public:   true,
 | 
				
			||||||
 | 
							Name:     "testcreateintcollision",
 | 
				
			||||||
 | 
							Version:  "testcreateintcollision",
 | 
				
			||||||
 | 
						}).Id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						w := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if createTask(api.CreateTaskRequest{
 | 
				
			||||||
 | 
							Project:  pid,
 | 
				
			||||||
 | 
							Hash64:   123,
 | 
				
			||||||
 | 
							Priority: 1,
 | 
				
			||||||
 | 
							Recipe:   "{}",
 | 
				
			||||||
 | 
						}, w).Ok != true {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := createTask(api.CreateTaskRequest{
 | 
				
			||||||
 | 
							Project:  pid,
 | 
				
			||||||
 | 
							Hash64:   123,
 | 
				
			||||||
 | 
							Priority: 1,
 | 
				
			||||||
 | 
							Recipe:   "{}",
 | 
				
			||||||
 | 
						}, w)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if resp.Ok != false {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println(resp.Message)
 | 
				
			||||||
 | 
						if len(resp.Message) <= 0 {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateStringCollision(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pid := createProject(api.CreateProjectRequest{
 | 
				
			||||||
 | 
							Priority: 1,
 | 
				
			||||||
 | 
							GitRepo:  "testcreatestringcollision",
 | 
				
			||||||
 | 
							CloneUrl: "testcreatestringcollision",
 | 
				
			||||||
 | 
							Motd:     "testcreatestringcollision",
 | 
				
			||||||
 | 
							Public:   true,
 | 
				
			||||||
 | 
							Name:     "testcreatestringcollision",
 | 
				
			||||||
 | 
							Version:  "testcreatestringcollision",
 | 
				
			||||||
 | 
						}).Id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						w := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if createTask(api.CreateTaskRequest{
 | 
				
			||||||
 | 
							Project:      pid,
 | 
				
			||||||
 | 
							UniqueString: "Hello, world",
 | 
				
			||||||
 | 
							Priority:     1,
 | 
				
			||||||
 | 
							Recipe:       "{}",
 | 
				
			||||||
 | 
						}, w).Ok != true {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := createTask(api.CreateTaskRequest{
 | 
				
			||||||
 | 
							Project:      pid,
 | 
				
			||||||
 | 
							UniqueString: "Hello, world",
 | 
				
			||||||
 | 
							Priority:     1,
 | 
				
			||||||
 | 
							Recipe:       "{}",
 | 
				
			||||||
 | 
						}, w)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !createTask(api.CreateTaskRequest{
 | 
				
			||||||
 | 
							Project:      pid,
 | 
				
			||||||
 | 
							UniqueString: "This one should work",
 | 
				
			||||||
 | 
							Priority:     1,
 | 
				
			||||||
 | 
							Recipe:       "{}",
 | 
				
			||||||
 | 
						}, w).Ok {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if resp.Ok != false {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println(resp.Message)
 | 
				
			||||||
 | 
						if len(resp.Message) <= 0 {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createTask(request api.CreateTaskRequest, worker *storage.Worker) *api.CreateTaskResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r := Post("/task/create", request, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var resp api.CreateTaskResponse
 | 
						var resp api.CreateTaskResponse
 | 
				
			||||||
	data, _ := ioutil.ReadAll(r.Body)
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
@ -395,9 +518,9 @@ func createTask(request api.CreateTaskRequest) *api.CreateTaskResponse {
 | 
				
			|||||||
	return &resp
 | 
						return &resp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getTask(wid *uuid.UUID) *api.GetTaskResponse {
 | 
					func getTask(worker *storage.Worker) *api.GetTaskResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := Get(fmt.Sprintf("/task/get?wid=%s", wid))
 | 
						r := Get(fmt.Sprintf("/task/get"), worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var resp api.GetTaskResponse
 | 
						var resp api.GetTaskResponse
 | 
				
			||||||
	data, _ := ioutil.ReadAll(r.Body)
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
@ -407,9 +530,9 @@ func getTask(wid *uuid.UUID) *api.GetTaskResponse {
 | 
				
			|||||||
	return &resp
 | 
						return &resp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getTaskFromProject(project int64, wid *uuid.UUID) *api.GetTaskResponse {
 | 
					func getTaskFromProject(project int64, worker *storage.Worker) *api.GetTaskResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := Get(fmt.Sprintf("/task/get/%d?wid=%s", project, wid))
 | 
						r := Get(fmt.Sprintf("/task/get/%d", project), worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var resp api.GetTaskResponse
 | 
						var resp api.GetTaskResponse
 | 
				
			||||||
	data, _ := ioutil.ReadAll(r.Body)
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
@ -418,3 +541,15 @@ func getTaskFromProject(project int64, wid *uuid.UUID) *api.GetTaskResponse {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return &resp
 | 
						return &resp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func releaseTask(request api.ReleaseTaskRequest, worker *storage.Worker) *api.ReleaseTaskResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r := Post("/task/release", request, worker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var resp api.ReleaseTaskResponse
 | 
				
			||||||
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
 | 
						err := json.Unmarshal(data, &resp)
 | 
				
			||||||
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &resp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,12 +7,15 @@ import (
 | 
				
			|||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"src/task_tracker/api"
 | 
						"src/task_tracker/api"
 | 
				
			||||||
 | 
						"src/task_tracker/storage"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestCreateGetWorker(t *testing.T) {
 | 
					func TestCreateGetWorker(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp, r := createWorker(api.CreateWorkerRequest{})
 | 
						resp, r := createWorker(api.CreateWorkerRequest{
 | 
				
			||||||
 | 
							Alias: "my_worker_alias",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if r.StatusCode != 200 {
 | 
						if r.StatusCode != 200 {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
@ -22,12 +25,12 @@ func TestCreateGetWorker(t *testing.T) {
 | 
				
			|||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getResp, r := getWorker(resp.WorkerId.String())
 | 
						getResp, r := getWorker(resp.Worker.Id.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if r.StatusCode != 200 {
 | 
						if r.StatusCode != 200 {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if resp.WorkerId != getResp.Worker.Id {
 | 
						if resp.Worker.Id != getResp.Worker.Id {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -37,6 +40,9 @@ func TestCreateGetWorker(t *testing.T) {
 | 
				
			|||||||
	if len(getResp.Worker.Identity.UserAgent) <= 0 {
 | 
						if len(getResp.Worker.Identity.UserAgent) <= 0 {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if resp.Worker.Alias != "my_worker_alias" {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestGetWorkerNotFound(t *testing.T) {
 | 
					func TestGetWorkerNotFound(t *testing.T) {
 | 
				
			||||||
@ -70,7 +76,7 @@ func TestGrantAccessFailedProjectConstraint(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	wid := genWid()
 | 
						wid := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := grantAccess(wid, 38274593)
 | 
						resp := grantAccess(&wid.Id, 38274593)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if resp.Ok != false {
 | 
						if resp.Ok != false {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
@ -82,9 +88,9 @@ func TestGrantAccessFailedProjectConstraint(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestRemoveAccessFailedProjectConstraint(t *testing.T) {
 | 
					func TestRemoveAccessFailedProjectConstraint(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wid := genWid()
 | 
						worker := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := removeAccess(wid, 38274593)
 | 
						resp := removeAccess(&worker.Id, 38274593)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if resp.Ok != false {
 | 
						if resp.Ok != false {
 | 
				
			||||||
		t.Error()
 | 
							t.Error()
 | 
				
			||||||
@ -138,8 +144,43 @@ func TestGrantAccessFailedWorkerConstraint(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUpdateAliasValid(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wid := genWid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						updateResp := updateWorker(api.UpdateWorkerRequest{
 | 
				
			||||||
 | 
							Alias: "new alias",
 | 
				
			||||||
 | 
						}, wid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if updateResp.Ok != true {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						w, _ := getWorker(wid.Id.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if w.Worker.Alias != "new alias" {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateWorkerAliasInvalid(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp, _ := createWorker(api.CreateWorkerRequest{
 | 
				
			||||||
 | 
							Alias: "unassigned", //reserved alias
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if resp.Ok != false {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(resp.Message) <= 0 {
 | 
				
			||||||
 | 
							t.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func createWorker(req api.CreateWorkerRequest) (*api.CreateWorkerResponse, *http.Response) {
 | 
					func createWorker(req api.CreateWorkerRequest) (*api.CreateWorkerResponse, *http.Response) {
 | 
				
			||||||
	r := Post("/worker/create", req)
 | 
						r := Post("/worker/create", req, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var resp *api.CreateWorkerResponse
 | 
						var resp *api.CreateWorkerResponse
 | 
				
			||||||
	data, _ := ioutil.ReadAll(r.Body)
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
@ -151,7 +192,7 @@ func createWorker(req api.CreateWorkerRequest) (*api.CreateWorkerResponse, *http
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func getWorker(id string) (*api.GetWorkerResponse, *http.Response) {
 | 
					func getWorker(id string) (*api.GetWorkerResponse, *http.Response) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := Get(fmt.Sprintf("/worker/get/%s", id))
 | 
						r := Get(fmt.Sprintf("/worker/get/%s", id), nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var resp *api.GetWorkerResponse
 | 
						var resp *api.GetWorkerResponse
 | 
				
			||||||
	data, _ := ioutil.ReadAll(r.Body)
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
@ -161,10 +202,10 @@ func getWorker(id string) (*api.GetWorkerResponse, *http.Response) {
 | 
				
			|||||||
	return resp, r
 | 
						return resp, r
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func genWid() *uuid.UUID {
 | 
					func genWid() *storage.Worker {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp, _ := createWorker(api.CreateWorkerRequest{})
 | 
						resp, _ := createWorker(api.CreateWorkerRequest{})
 | 
				
			||||||
	return &resp.WorkerId
 | 
						return resp.Worker
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func grantAccess(wid *uuid.UUID, project int64) *api.WorkerAccessResponse {
 | 
					func grantAccess(wid *uuid.UUID, project int64) *api.WorkerAccessResponse {
 | 
				
			||||||
@ -172,7 +213,7 @@ func grantAccess(wid *uuid.UUID, project int64) *api.WorkerAccessResponse {
 | 
				
			|||||||
	r := Post("/access/grant", api.WorkerAccessRequest{
 | 
						r := Post("/access/grant", api.WorkerAccessRequest{
 | 
				
			||||||
		WorkerId:  wid,
 | 
							WorkerId:  wid,
 | 
				
			||||||
		ProjectId: project,
 | 
							ProjectId: project,
 | 
				
			||||||
	})
 | 
						}, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var resp *api.WorkerAccessResponse
 | 
						var resp *api.WorkerAccessResponse
 | 
				
			||||||
	data, _ := ioutil.ReadAll(r.Body)
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
@ -187,7 +228,7 @@ func removeAccess(wid *uuid.UUID, project int64) *api.WorkerAccessResponse {
 | 
				
			|||||||
	r := Post("/access/remove", api.WorkerAccessRequest{
 | 
						r := Post("/access/remove", api.WorkerAccessRequest{
 | 
				
			||||||
		WorkerId:  wid,
 | 
							WorkerId:  wid,
 | 
				
			||||||
		ProjectId: project,
 | 
							ProjectId: project,
 | 
				
			||||||
	})
 | 
						}, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var resp *api.WorkerAccessResponse
 | 
						var resp *api.WorkerAccessResponse
 | 
				
			||||||
	data, _ := ioutil.ReadAll(r.Body)
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
@ -196,3 +237,15 @@ func removeAccess(wid *uuid.UUID, project int64) *api.WorkerAccessResponse {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return resp
 | 
						return resp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func updateWorker(request api.UpdateWorkerRequest, w *storage.Worker) *api.UpdateWorkerResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r := Post("/worker/update", request, w)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var resp *api.UpdateWorkerResponse
 | 
				
			||||||
 | 
						data, _ := ioutil.ReadAll(r.Body)
 | 
				
			||||||
 | 
						err := json.Unmarshal(data, &resp)
 | 
				
			||||||
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return resp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,26 +2,61 @@ package test
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"crypto"
 | 
				
			||||||
 | 
						"crypto/hmac"
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"src/task_tracker/config"
 | 
						"src/task_tracker/config"
 | 
				
			||||||
 | 
						"src/task_tracker/storage"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Post(path string, x interface{}) *http.Response {
 | 
					func Post(path string, x interface{}, worker *storage.Worker) *http.Response {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	body, err := json.Marshal(x)
 | 
						body, err := json.Marshal(x)
 | 
				
			||||||
	buf := bytes.NewBuffer(body)
 | 
						buf := bytes.NewBuffer(body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r, err := http.Post("http://"+config.Cfg.ServerAddr+path, "application/json", buf)
 | 
						req, err := http.NewRequest("POST", "http://"+config.Cfg.ServerAddr+path, buf)
 | 
				
			||||||
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if worker != nil {
 | 
				
			||||||
 | 
							mac := hmac.New(crypto.SHA256.New, worker.Secret)
 | 
				
			||||||
 | 
							mac.Write(body)
 | 
				
			||||||
 | 
							sig := hex.EncodeToString(mac.Sum(nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req.Header.Add("X-Worker-Id", worker.Id.String())
 | 
				
			||||||
 | 
							req.Header.Add("X-Signature", sig)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client := http.Client{}
 | 
				
			||||||
 | 
						r, err := client.Do(req)
 | 
				
			||||||
	handleErr(err)
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return r
 | 
						return r
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Get(path string) *http.Response {
 | 
					func Get(path string, worker *storage.Worker) *http.Response {
 | 
				
			||||||
	r, err := http.Get("http://" + config.Cfg.ServerAddr + path)
 | 
					
 | 
				
			||||||
 | 
						url := "http://" + config.Cfg.ServerAddr + path
 | 
				
			||||||
 | 
						req, err := http.NewRequest("GET", url, nil)
 | 
				
			||||||
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if worker != nil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fmt.Println(worker.Secret)
 | 
				
			||||||
 | 
							mac := hmac.New(crypto.SHA256.New, worker.Secret)
 | 
				
			||||||
 | 
							mac.Write([]byte(path))
 | 
				
			||||||
 | 
							sig := hex.EncodeToString(mac.Sum(nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req.Header.Add("X-Worker-Id", worker.Id.String())
 | 
				
			||||||
 | 
							req.Header.Add("X-Signature", sig)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client := http.Client{}
 | 
				
			||||||
 | 
						r, err := client.Do(req)
 | 
				
			||||||
	handleErr(err)
 | 
						handleErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return r
 | 
						return r
 | 
				
			||||||
 | 
				
			|||||||
@ -26,9 +26,10 @@ CREATE TABLE worker_identity
 | 
				
			|||||||
CREATE TABLE worker
 | 
					CREATE TABLE worker
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
  id       TEXT PRIMARY KEY,
 | 
					  id       TEXT PRIMARY KEY,
 | 
				
			||||||
  alias    TEXT DEFAULT NULL,
 | 
					  alias    TEXT,
 | 
				
			||||||
  created  INTEGER,
 | 
					  created  INTEGER,
 | 
				
			||||||
  identity INTEGER REFERENCES workerIdentity (id)
 | 
					  identity INTEGER REFERENCES worker_identity (id),
 | 
				
			||||||
 | 
					  secret   BYTEA
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE project
 | 
					CREATE TABLE project
 | 
				
			||||||
@ -61,7 +62,8 @@ CREATE TABLE task
 | 
				
			|||||||
  status          Status  DEFAULT 'new',
 | 
					  status          Status  DEFAULT 'new',
 | 
				
			||||||
  recipe          TEXT,
 | 
					  recipe          TEXT,
 | 
				
			||||||
  max_assign_time INTEGER DEFAULT 0,
 | 
					  max_assign_time INTEGER DEFAULT 0,
 | 
				
			||||||
  assign_time     INTEGER DEFAULT 0
 | 
					  assign_time     INTEGER DEFAULT 0,
 | 
				
			||||||
 | 
					  hash64          BIGINT UNIQUE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE log_entry
 | 
					CREATE TABLE log_entry
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import {Injectable} from '@angular/core';
 | 
					import {Injectable} from '@angular/core';
 | 
				
			||||||
import {HttpClient} from "@angular/common/http";
 | 
					import {HttpClient} from "@angular/common/http";
 | 
				
			||||||
 | 
					import {Project} from "./models/project";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class ApiService {
 | 
					export class ApiService {
 | 
				
			||||||
@ -26,4 +27,12 @@ export class ApiService {
 | 
				
			|||||||
    getProject(id: number) {
 | 
					    getProject(id: number) {
 | 
				
			||||||
        return this.http.get(this.url + "/project/get/" + id)
 | 
					        return this.http.get(this.url + "/project/get/" + id)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    createProject(project: Project) {
 | 
				
			||||||
 | 
					        return this.http.post(this.url + "/project/create", project)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    updateProject(project: Project) {
 | 
				
			||||||
 | 
					        return this.http.post(this.url + "/project/update/" + project.id, project)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,11 +2,11 @@
 | 
				
			|||||||
<ul>
 | 
					<ul>
 | 
				
			||||||
    <li><a [routerLink]="''">Index</a></li>
 | 
					    <li><a [routerLink]="''">Index</a></li>
 | 
				
			||||||
    <li><a [routerLink]="'log'">Logs</a></li>
 | 
					    <li><a [routerLink]="'log'">Logs</a></li>
 | 
				
			||||||
    <li><a [routerLink]="'project'">Project</a></li>
 | 
					 | 
				
			||||||
    <li><a [routerLink]="'projects'">list</a></li>
 | 
					    <li><a [routerLink]="'projects'">list</a></li>
 | 
				
			||||||
    <li><a [routerLink]="'new_project'">new project</a></li>
 | 
					    <li><a [routerLink]="'new_project'">new project</a></li>
 | 
				
			||||||
</ul>
 | 
					</ul>
 | 
				
			||||||
<!--</mat-toolbar>-->
 | 
					<!--</mat-toolbar>-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<messenger-snack-bar></messenger-snack-bar>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<router-outlet></router-outlet>
 | 
					<router-outlet></router-outlet>
 | 
				
			||||||
 | 
				
			|||||||
@ -20,18 +20,21 @@ import {
 | 
				
			|||||||
    MatPaginatorModule,
 | 
					    MatPaginatorModule,
 | 
				
			||||||
    MatSliderModule,
 | 
					    MatSliderModule,
 | 
				
			||||||
    MatSlideToggleModule,
 | 
					    MatSlideToggleModule,
 | 
				
			||||||
 | 
					    MatSnackBarModule,
 | 
				
			||||||
    MatSortModule,
 | 
					    MatSortModule,
 | 
				
			||||||
    MatTableModule,
 | 
					    MatTableModule,
 | 
				
			||||||
    MatToolbarModule,
 | 
					    MatToolbarModule,
 | 
				
			||||||
    MatTreeModule
 | 
					    MatTreeModule
 | 
				
			||||||
} from "@angular/material";
 | 
					} from "@angular/material";
 | 
				
			||||||
import {ApiService} from "./api.service";
 | 
					import {ApiService} from "./api.service";
 | 
				
			||||||
 | 
					import {MessengerService} from "./messenger.service";
 | 
				
			||||||
import {HttpClientModule} from "@angular/common/http";
 | 
					import {HttpClientModule} from "@angular/common/http";
 | 
				
			||||||
import {ProjectDashboardComponent} from './project-dashboard/project-dashboard.component';
 | 
					import {ProjectDashboardComponent} from './project-dashboard/project-dashboard.component';
 | 
				
			||||||
import {ProjectListComponent} from './project-list/project-list.component';
 | 
					import {ProjectListComponent} from './project-list/project-list.component';
 | 
				
			||||||
import {CreateProjectComponent} from './create-project/create-project.component';
 | 
					import {CreateProjectComponent} from './create-project/create-project.component';
 | 
				
			||||||
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
 | 
					import {FormsModule, ReactiveFormsModule} from "@angular/forms";
 | 
				
			||||||
import {UpdateProjectComponent} from './update-project/update-project.component';
 | 
					import {UpdateProjectComponent} from './update-project/update-project.component';
 | 
				
			||||||
 | 
					import {SnackBarComponent} from "./messenger/snack-bar.component";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@NgModule({
 | 
					@NgModule({
 | 
				
			||||||
    declarations: [
 | 
					    declarations: [
 | 
				
			||||||
@ -40,7 +43,8 @@ import {UpdateProjectComponent} from './update-project/update-project.component'
 | 
				
			|||||||
        ProjectDashboardComponent,
 | 
					        ProjectDashboardComponent,
 | 
				
			||||||
        ProjectListComponent,
 | 
					        ProjectListComponent,
 | 
				
			||||||
        CreateProjectComponent,
 | 
					        CreateProjectComponent,
 | 
				
			||||||
        UpdateProjectComponent
 | 
					        UpdateProjectComponent,
 | 
				
			||||||
 | 
					        SnackBarComponent,
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    imports: [
 | 
					    imports: [
 | 
				
			||||||
        BrowserModule,
 | 
					        BrowserModule,
 | 
				
			||||||
@ -65,12 +69,17 @@ import {UpdateProjectComponent} from './update-project/update-project.component'
 | 
				
			|||||||
        MatSliderModule,
 | 
					        MatSliderModule,
 | 
				
			||||||
        MatSlideToggleModule,
 | 
					        MatSlideToggleModule,
 | 
				
			||||||
        MatCheckboxModule,
 | 
					        MatCheckboxModule,
 | 
				
			||||||
        MatDividerModule
 | 
					        MatDividerModule,
 | 
				
			||||||
 | 
					        MatSnackBarModule,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    exports: [],
 | 
					    exports: [],
 | 
				
			||||||
    providers: [
 | 
					    providers: [
 | 
				
			||||||
        ApiService,
 | 
					        ApiService,
 | 
				
			||||||
 | 
					        MessengerService,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    entryComponents: [
 | 
				
			||||||
 | 
					        SnackBarComponent,
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    bootstrap: [AppComponent]
 | 
					    bootstrap: [AppComponent]
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
    <mat-card-subtitle></mat-card-subtitle>
 | 
					    <mat-card-subtitle></mat-card-subtitle>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <mat-card-content>
 | 
					    <mat-card-content>
 | 
				
			||||||
        <form>
 | 
					        <form (ngSubmit)="onSubmit()">
 | 
				
			||||||
            <mat-form-field appearance="outline">
 | 
					            <mat-form-field appearance="outline">
 | 
				
			||||||
                <mat-label>Project name</mat-label>
 | 
					                <mat-label>Project name</mat-label>
 | 
				
			||||||
                <input type="text" matInput [(ngModel)]="project.name" name="name" placeholder="Project name">
 | 
					                <input type="text" matInput [(ngModel)]="project.name" name="name" placeholder="Project name">
 | 
				
			||||||
@ -29,11 +29,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <input type="hidden" name="version" value="{{project.version}}">
 | 
					            <input type="hidden" name="version" value="{{project.version}}">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <input type="submit" value="Create">
 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
    </mat-card-content>
 | 
					    </mat-card-content>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <mat-card-actions>
 | 
					 | 
				
			||||||
        <button>Create</button>
 | 
					 | 
				
			||||||
    </mat-card-actions>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
</mat-card>
 | 
					</mat-card>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,9 @@
 | 
				
			|||||||
import {Component, OnInit} from '@angular/core';
 | 
					import {Component, OnInit} from '@angular/core';
 | 
				
			||||||
import {Project} from "../models/project";
 | 
					import {Project} from "../models/project";
 | 
				
			||||||
 | 
					import {ApiService} from "../api.service";
 | 
				
			||||||
 | 
					import {MessengerService} from "../messenger.service";
 | 
				
			||||||
 | 
					import {Router} from "@angular/router";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
    selector: 'app-create-project',
 | 
					    selector: 'app-create-project',
 | 
				
			||||||
@ -10,7 +14,9 @@ export class CreateProjectComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private project = new Project();
 | 
					    private project = new Project();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor() {
 | 
					    constructor(private apiService: ApiService,
 | 
				
			||||||
 | 
					                private messengerService: MessengerService,
 | 
				
			||||||
 | 
					                private router: Router) {
 | 
				
			||||||
        this.project.name = "test";
 | 
					        this.project.name = "test";
 | 
				
			||||||
        this.project.public = true;
 | 
					        this.project.public = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -18,4 +24,16 @@ export class CreateProjectComponent implements OnInit {
 | 
				
			|||||||
    ngOnInit() {
 | 
					    ngOnInit() {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    onSubmit() {
 | 
				
			||||||
 | 
					        this.apiService.createProject(this.project).subscribe(
 | 
				
			||||||
 | 
					            data => {
 | 
				
			||||||
 | 
					                this.router.navigateByUrl("/project/" + data["id"]);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            error => {
 | 
				
			||||||
 | 
					                console.log(error.error.message);
 | 
				
			||||||
 | 
					                this.messengerService.show(error.error.message);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										22
									
								
								web/angular/src/app/messenger.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								web/angular/src/app/messenger.service.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					import {Injectable} from '@angular/core';
 | 
				
			||||||
 | 
					import {Subject} from "rxjs";
 | 
				
			||||||
 | 
					import {MessengerState} from "./messenger/messenger";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Injectable()
 | 
				
			||||||
 | 
					export class MessengerService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public messengerSubject = new Subject<MessengerState>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    show(message: string) {
 | 
				
			||||||
 | 
					        this.messengerSubject.next({
 | 
				
			||||||
 | 
					            message: message,
 | 
				
			||||||
 | 
					            hidden: false,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hide() {
 | 
				
			||||||
 | 
					        this.messengerSubject.next({
 | 
				
			||||||
 | 
					            hidden: true,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								web/angular/src/app/messenger/messenger.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								web/angular/src/app/messenger/messenger.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					export class MessengerState {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hidden: boolean;
 | 
				
			||||||
 | 
					    message?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								web/angular/src/app/messenger/snack-bar.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								web/angular/src/app/messenger/snack-bar.component.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					import {Component, OnInit} from '@angular/core';
 | 
				
			||||||
 | 
					import {MessengerService} from "../messenger.service";
 | 
				
			||||||
 | 
					import {MessengerState} from "./messenger";
 | 
				
			||||||
 | 
					import {Subscription} from "rxjs";
 | 
				
			||||||
 | 
					import {MatSnackBar, MatSnackBarConfig} from "@angular/material";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					    selector: 'messenger-snack-bar',
 | 
				
			||||||
 | 
					    templateUrl: 'messenger-snack-bar.html',
 | 
				
			||||||
 | 
					    styleUrls: ['messenger-snack-bar.css'],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class SnackBarComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private subscription: Subscription;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(private messengerService: MessengerService, private snackBar: MatSnackBar) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ngOnInit() {
 | 
				
			||||||
 | 
					        this.subscription = this.messengerService.messengerSubject
 | 
				
			||||||
 | 
					            .subscribe((state: MessengerState) => {
 | 
				
			||||||
 | 
					                if (state.hidden) {
 | 
				
			||||||
 | 
					                    this.snackBar.dismiss();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    this.snackBar.open(state.message, "Close", <MatSnackBarConfig>{
 | 
				
			||||||
 | 
					                        duration: 10 * 1000,
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
export class Project {
 | 
					export class Project {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public id: number;
 | 
				
			||||||
    public priority: number;
 | 
					    public priority: number;
 | 
				
			||||||
    public motd: string;
 | 
					    public motd: string;
 | 
				
			||||||
    public name: string;
 | 
					    public name: string;
 | 
				
			||||||
 | 
				
			|||||||
@ -37,6 +37,8 @@
 | 
				
			|||||||
                <pre>{{projectStats | json}}</pre>
 | 
					                <pre>{{projectStats | json}}</pre>
 | 
				
			||||||
            </mat-expansion-panel>
 | 
					            </mat-expansion-panel>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <a [routerLink]="'/project/' + projectStats.project.id + '/update'">Update</a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        </mat-card-content>
 | 
					        </mat-card-content>
 | 
				
			||||||
    </mat-card>
 | 
					    </mat-card>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
				
			|||||||
@ -56,14 +56,14 @@ export class ProjectDashboardComponent implements OnInit {
 | 
				
			|||||||
    setupStatusPieChart() {
 | 
					    setupStatusPieChart() {
 | 
				
			||||||
        let tooltip = d3.select("#stooltip");
 | 
					        let tooltip = d3.select("#stooltip");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.statusSvg = d3.select('#status')
 | 
					        this.statusSvg = d3.select("#status")
 | 
				
			||||||
            .append('svg')
 | 
					            .append('svg')
 | 
				
			||||||
            .attr('width', this.pieWidth)
 | 
					            .attr('width', this.pieWidth)
 | 
				
			||||||
            .attr('height', this.pieHeight)
 | 
					            .attr('height', this.pieHeight)
 | 
				
			||||||
            .append("g")
 | 
					            .append("g")
 | 
				
			||||||
            .attr("transform", "translate(" + this.pieRadius + "," + this.pieRadius + ")");
 | 
					            .attr("transform", "translate(" + this.pieRadius + "," + this.pieRadius + ")");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.statusPath = this.statusSvg.selectAll("path")
 | 
					        this.statusPath = this.statusSvg.selectAll()
 | 
				
			||||||
            .data(this.pieFun(this.statusData))
 | 
					            .data(this.pieFun(this.statusData))
 | 
				
			||||||
            .enter()
 | 
					            .enter()
 | 
				
			||||||
            .append('path')
 | 
					            .append('path')
 | 
				
			||||||
@ -76,14 +76,14 @@ export class ProjectDashboardComponent implements OnInit {
 | 
				
			|||||||
    setupAssigneesPieChart() {
 | 
					    setupAssigneesPieChart() {
 | 
				
			||||||
        let tooltip = d3.select("#atooltip");
 | 
					        let tooltip = d3.select("#atooltip");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.assigneesSvg = d3.select('#assignees')
 | 
					        this.assigneesSvg = d3.select("#assignees")
 | 
				
			||||||
            .append('svg')
 | 
					            .append('svg')
 | 
				
			||||||
            .attr('width', this.pieWidth)
 | 
					            .attr('width', this.pieWidth)
 | 
				
			||||||
            .attr('height', this.pieHeight)
 | 
					            .attr('height', this.pieHeight)
 | 
				
			||||||
            .append("g")
 | 
					            .append("g")
 | 
				
			||||||
            .attr("transform", "translate(" + this.pieRadius + "," + this.pieRadius + ")");
 | 
					            .attr("transform", "translate(" + this.pieRadius + "," + this.pieRadius + ")");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.assigneesPath = this.assigneesSvg.selectAll("path")
 | 
					        this.assigneesPath = this.assigneesSvg.selectAll()
 | 
				
			||||||
            .data(this.pieFun(this.assigneesData))
 | 
					            .data(this.pieFun(this.assigneesData))
 | 
				
			||||||
            .enter()
 | 
					            .enter()
 | 
				
			||||||
            .append('path')
 | 
					            .append('path')
 | 
				
			||||||
@ -229,10 +229,10 @@ export class ProjectDashboardComponent implements OnInit {
 | 
				
			|||||||
            {label: "Failed", count: this.projectStats["failed_task_count"]},
 | 
					            {label: "Failed", count: this.projectStats["failed_task_count"]},
 | 
				
			||||||
            {label: "Closed", count: this.projectStats["closed_task_count"]},
 | 
					            {label: "Closed", count: this.projectStats["closed_task_count"]},
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
        this.assigneesData = _.map(this.projectStats["assignees"], (assignedTasks) => {
 | 
					        this.assigneesData = _.map(this.projectStats["assignees"], assignedTask => {
 | 
				
			||||||
            return {
 | 
					            return {
 | 
				
			||||||
                label: assignedTasks["assignee"] == "00000000-0000-0000-0000-000000000000" ? "unassigned" : assignedTasks["assignee"],
 | 
					                label: assignedTask["assignee"],
 | 
				
			||||||
                count: assignedTasks["task_count"]
 | 
					                count: assignedTask["task_count"],
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -256,6 +256,16 @@ export class ProjectDashboardComponent implements OnInit {
 | 
				
			|||||||
        ];
 | 
					        ];
 | 
				
			||||||
        this.assigneesData = [
 | 
					        this.assigneesData = [
 | 
				
			||||||
            {label: 'null', count: 0},
 | 
					            {label: 'null', count: 0},
 | 
				
			||||||
 | 
					            {label: 'null', count: 0},
 | 
				
			||||||
 | 
					            {label: 'null', count: 0},
 | 
				
			||||||
 | 
					            {label: 'null', count: 0},
 | 
				
			||||||
 | 
					            {label: 'null', count: 0},
 | 
				
			||||||
 | 
					            {label: 'null', count: 0},
 | 
				
			||||||
 | 
					            {label: 'null', count: 0},
 | 
				
			||||||
 | 
					            {label: 'null', count: 0},
 | 
				
			||||||
 | 
					            {label: 'null', count: 0},
 | 
				
			||||||
 | 
					            {label: 'null', count: 0},
 | 
				
			||||||
 | 
					            {label: 'null', count: 0},
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.setupStatusPieChart();
 | 
					        this.setupStatusPieChart();
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@
 | 
				
			|||||||
                <pre>{{stats.project | json}}</pre>
 | 
					                <pre>{{stats.project | json}}</pre>
 | 
				
			||||||
                <div style="display: flex;">
 | 
					                <div style="display: flex;">
 | 
				
			||||||
                    <a [routerLink]="'/project/' + stats.project.id">Dashboard</a>
 | 
					                    <a [routerLink]="'/project/' + stats.project.id">Dashboard</a>
 | 
				
			||||||
 | 
					                    <a [routerLink]="'/project/' + stats.project.id + '/update'">Update</a>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </mat-expansion-panel>
 | 
					            </mat-expansion-panel>
 | 
				
			||||||
        </mat-accordion>
 | 
					        </mat-accordion>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,29 +1,29 @@
 | 
				
			|||||||
<mat-card>
 | 
					<mat-card>
 | 
				
			||||||
    <mat-card-title>Update project</mat-card-title>
 | 
					    <mat-card-title>Update project</mat-card-title>
 | 
				
			||||||
    <mat-card-subtitle>Changes are saved in real time</mat-card-subtitle>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <mat-card-content>
 | 
					    <mat-card-content>
 | 
				
			||||||
        <form>
 | 
					        <form (ngSubmit)="onSubmit()" *ngIf="project != undefined">
 | 
				
			||||||
            <mat-form-field>
 | 
					            <mat-form-field appearance="outline">
 | 
				
			||||||
                <input type="text" matInput [(ngModel)]="project.name" name="name" placeholder="Name">
 | 
					                <input type="text" matInput [(ngModel)]="project.name" name="name" placeholder="Name">
 | 
				
			||||||
            </mat-form-field>
 | 
					            </mat-form-field>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <mat-form-field>
 | 
					            <mat-form-field appearance="outline">
 | 
				
			||||||
                <textarea matInput placeholder="Message of the day"></textarea>
 | 
					                <textarea matInput [(ngModel)]="project.motd" placeholder="Message of the day" name="motd"></textarea>
 | 
				
			||||||
            </mat-form-field>
 | 
					            </mat-form-field>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <mat-form-field>
 | 
					            <mat-form-field appearance="outline">
 | 
				
			||||||
                <input type="text" matInput [(ngModel)]="project.clone_url" name="clone_url"
 | 
					                <input type="text" matInput [(ngModel)]="project.clone_url" name="clone_url"
 | 
				
			||||||
                       placeholder="Git clone url">
 | 
					                       placeholder="Git clone url">
 | 
				
			||||||
            </mat-form-field>
 | 
					            </mat-form-field>
 | 
				
			||||||
            <mat-form-field>
 | 
					            <mat-form-field appearance="outline">
 | 
				
			||||||
                <input type="text" matInput [(ngModel)]="project.git_repo" name="clone_url"
 | 
					                <input type="text" matInput [(ngModel)]="project.git_repo" name="git_repo"
 | 
				
			||||||
                       placeholder='Full repository name (e.g. "simon987/task_tracker")'>
 | 
					                       placeholder='Full repository name (e.g. "simon987/task_tracker")'>
 | 
				
			||||||
                <mat-hint align="start">Changes on the <strong>master</strong> branch will be tracked if webhooks are
 | 
					                <mat-hint align="start">Changes on the <strong>master</strong> branch will be tracked if webhooks are
 | 
				
			||||||
                    enabled
 | 
					                    enabled
 | 
				
			||||||
                </mat-hint>
 | 
					                </mat-hint>
 | 
				
			||||||
            </mat-form-field>
 | 
					            </mat-form-field>
 | 
				
			||||||
            <input type="hidden" name="version" value="{{project.version}}">
 | 
					
 | 
				
			||||||
 | 
					            <input type="submit" value="Update">
 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
    </mat-card-content>
 | 
					    </mat-card-content>
 | 
				
			||||||
</mat-card>
 | 
					</mat-card>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,8 @@
 | 
				
			|||||||
import {Component, OnInit} from '@angular/core';
 | 
					import {Component, OnInit} from '@angular/core';
 | 
				
			||||||
import {Project} from "../models/project";
 | 
					import {Project} from "../models/project";
 | 
				
			||||||
import {ApiService} from "../api.service";
 | 
					import {ApiService} from "../api.service";
 | 
				
			||||||
import {ActivatedRoute} from "@angular/router";
 | 
					import {ActivatedRoute, Router} from "@angular/router";
 | 
				
			||||||
 | 
					import {MessengerService} from "../messenger.service";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
    selector: 'app-update-project',
 | 
					    selector: 'app-update-project',
 | 
				
			||||||
@ -10,7 +11,10 @@ import {ActivatedRoute} from "@angular/router";
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class UpdateProjectComponent implements OnInit {
 | 
					export class UpdateProjectComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(private apiService: ApiService, private route: ActivatedRoute) {
 | 
					    constructor(private apiService: ApiService,
 | 
				
			||||||
 | 
					                private route: ActivatedRoute,
 | 
				
			||||||
 | 
					                private messengerService: MessengerService,
 | 
				
			||||||
 | 
					                private router: Router) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private project: Project;
 | 
					    private project: Project;
 | 
				
			||||||
@ -26,15 +30,28 @@ export class UpdateProjectComponent implements OnInit {
 | 
				
			|||||||
    private getProject() {
 | 
					    private getProject() {
 | 
				
			||||||
        this.apiService.getProject(this.projectId).subscribe(data => {
 | 
					        this.apiService.getProject(this.projectId).subscribe(data => {
 | 
				
			||||||
            this.project = <Project>{
 | 
					            this.project = <Project>{
 | 
				
			||||||
 | 
					                id: data["project"]["id"],
 | 
				
			||||||
                name: data["project"]["name"],
 | 
					                name: data["project"]["name"],
 | 
				
			||||||
                clone_url: data["project"]["clone_url"],
 | 
					                clone_url: data["project"]["clone_url"],
 | 
				
			||||||
                git_repo: data["project"]["git_repo"],
 | 
					                git_repo: data["project"]["git_repo"],
 | 
				
			||||||
                motd: data["project"]["motd"],
 | 
					                motd: data["project"]["motd"],
 | 
				
			||||||
                priority: data["project"]["priority"],
 | 
					                priority: data["project"]["priority"],
 | 
				
			||||||
                version: data["project"]["version"]
 | 
					                version: data["project"]["version"],
 | 
				
			||||||
 | 
					                public: data["project"]["public"],
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    onSubmit() {
 | 
				
			||||||
 | 
					        this.apiService.updateProject(this.project).subscribe(
 | 
				
			||||||
 | 
					            data => {
 | 
				
			||||||
 | 
					                this.router.navigateByUrl("/project/" + this.project.id);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            error => {
 | 
				
			||||||
 | 
					                console.log(error.error.message);
 | 
				
			||||||
 | 
					                this.messengerService.show(error.error.message);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user