mirror of
				https://github.com/simon987/task_tracker.git
				synced 2025-10-31 07:56:54 +00:00 
			
		
		
		
	Added public project attribute & worker access api endpoints
This commit is contained in:
		
							parent
							
								
									1d656099f5
								
							
						
					
					
						commit
						f250a2180c
					
				| @ -50,6 +50,9 @@ func New() *WebAPI { | ||||
| 	api.router.POST("/worker/create", LogRequestMiddleware(api.WorkerCreate)) | ||||
| 	api.router.GET("/worker/get/:id", LogRequestMiddleware(api.WorkerGet)) | ||||
| 
 | ||||
| 	api.router.POST("/access/grant", LogRequestMiddleware(api.WorkerGrantAccess)) | ||||
| 	api.router.POST("/access/remove", LogRequestMiddleware(api.WorkerRemoveAccess)) | ||||
| 
 | ||||
| 	api.router.POST("/project/create", LogRequestMiddleware(api.ProjectCreate)) | ||||
| 	api.router.GET("/project/get/:id", LogRequestMiddleware(api.ProjectGet)) | ||||
| 	api.router.GET("/project/stats/:id", LogRequestMiddleware(api.ProjectGetStats)) | ||||
|  | ||||
| @ -13,6 +13,7 @@ type CreateProjectRequest struct { | ||||
| 	Version  string `json:"version"` | ||||
| 	Priority int64  `json:"priority"` | ||||
| 	Motd     string `json:"motd"` | ||||
| 	Public   bool   `json:"public"` | ||||
| } | ||||
| 
 | ||||
| type CreateProjectResponse struct { | ||||
| @ -51,6 +52,7 @@ func (api *WebAPI) ProjectCreate(r *Request) { | ||||
| 			GitRepo:  createReq.GitRepo, | ||||
| 			Priority: createReq.Priority, | ||||
| 			Motd:     createReq.Motd, | ||||
| 			Public:   createReq.Public, | ||||
| 		} | ||||
| 
 | ||||
| 		if isValidProject(project) { | ||||
|  | ||||
							
								
								
									
										36
									
								
								api/task.go
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								api/task.go
									
									
									
									
									
								
							| @ -9,10 +9,11 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| type CreateTaskRequest struct { | ||||
| 	Project    int64  `json:"project"` | ||||
| 	MaxRetries int64  `json:"max_retries"` | ||||
| 	Recipe     string `json:"recipe"` | ||||
| 	Priority   int64  `json:"priority"` | ||||
| 	Project       int64  `json:"project"` | ||||
| 	MaxRetries    int64  `json:"max_retries"` | ||||
| 	Recipe        string `json:"recipe"` | ||||
| 	Priority      int64  `json:"priority"` | ||||
| 	MaxAssignTime int64  `json:"max_assign_time"` | ||||
| } | ||||
| 
 | ||||
| type ReleaseTaskRequest struct { | ||||
| @ -43,9 +44,11 @@ func (api *WebAPI) TaskCreate(r *Request) { | ||||
| 	if r.GetJson(&createReq) { | ||||
| 
 | ||||
| 		task := &storage.Task{ | ||||
| 			MaxRetries: createReq.MaxRetries, | ||||
| 			Recipe:     createReq.Recipe, | ||||
| 			Priority:   createReq.Priority, | ||||
| 			MaxRetries:    createReq.MaxRetries, | ||||
| 			Recipe:        createReq.Recipe, | ||||
| 			Priority:      createReq.Priority, | ||||
| 			AssignTime:    0, | ||||
| 			MaxAssignTime: createReq.MaxAssignTime, | ||||
| 		} | ||||
| 
 | ||||
| 		if isTaskValid(task) { | ||||
| @ -99,10 +102,21 @@ func (api *WebAPI) TaskGetFromProject(r *Request) { | ||||
| 	handleErr(err, r) | ||||
| 	task := api.Database.GetTaskFromProject(worker, int64(project)) | ||||
| 
 | ||||
| 	r.OkJson(GetTaskResponse{ | ||||
| 		Ok:   true, | ||||
| 		Task: task, | ||||
| 	}) | ||||
| 	if task == nil { | ||||
| 
 | ||||
| 		r.OkJson(GetTaskResponse{ | ||||
| 			Ok:      false, | ||||
| 			Message: "No task available", | ||||
| 		}) | ||||
| 
 | ||||
| 	} else { | ||||
| 
 | ||||
| 		r.OkJson(GetTaskResponse{ | ||||
| 			Ok:   true, | ||||
| 			Task: task, | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func (api *WebAPI) TaskGet(r *Request) { | ||||
|  | ||||
| @ -22,6 +22,16 @@ type GetWorkerResponse struct { | ||||
| 	Worker  *storage.Worker `json:"worker,omitempty"` | ||||
| } | ||||
| 
 | ||||
| type WorkerAccessRequest struct { | ||||
| 	WorkerId  *uuid.UUID `json:"worker_id"` | ||||
| 	ProjectId int64      `json:"project_id"` | ||||
| } | ||||
| 
 | ||||
| type WorkerAccessResponse struct { | ||||
| 	Ok      bool   `json:"ok"` | ||||
| 	Message string `json:"message"` | ||||
| } | ||||
| 
 | ||||
| func (api *WebAPI) WorkerCreate(r *Request) { | ||||
| 
 | ||||
| 	workerReq := &CreateWorkerRequest{} | ||||
| @ -86,6 +96,46 @@ func (api *WebAPI) WorkerGet(r *Request) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (api *WebAPI) WorkerGrantAccess(r *Request) { | ||||
| 
 | ||||
| 	req := &WorkerAccessRequest{} | ||||
| 	if r.GetJson(req) { | ||||
| 
 | ||||
| 		ok := api.Database.GrantAccess(req.WorkerId, req.ProjectId) | ||||
| 
 | ||||
| 		if ok { | ||||
| 			r.OkJson(WorkerAccessResponse{ | ||||
| 				Ok: true, | ||||
| 			}) | ||||
| 		} else { | ||||
| 			r.OkJson(WorkerAccessResponse{ | ||||
| 				Ok:      false, | ||||
| 				Message: "Worker already has access to this project", | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (api *WebAPI) WorkerRemoveAccess(r *Request) { | ||||
| 
 | ||||
| 	req := &WorkerAccessRequest{} | ||||
| 	if r.GetJson(req) { | ||||
| 
 | ||||
| 		ok := api.Database.RemoveAccess(req.WorkerId, req.ProjectId) | ||||
| 
 | ||||
| 		if ok { | ||||
| 			r.OkJson(WorkerAccessResponse{ | ||||
| 				Ok: true, | ||||
| 			}) | ||||
| 		} else { | ||||
| 			r.OkJson(WorkerAccessResponse{ | ||||
| 				Ok:      false, | ||||
| 				Message: "Worker did not have access to this project", | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (api *WebAPI) workerCreate(request *CreateWorkerRequest, identity *storage.Identity) (uuid.UUID, error) { | ||||
| 
 | ||||
| 	worker := storage.Worker{ | ||||
|  | ||||
							
								
								
									
										44
									
								
								schema.sql
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								schema.sql
									
									
									
									
									
								
							| @ -1,18 +1,20 @@ | ||||
| DROP TABLE IF EXISTS workeridentity, Worker, Project, Task, log_entry; | ||||
| DROP TABLE IF EXISTS worker_identity, worker, project, task, log_entry, | ||||
|   worker_has_access_to_project; | ||||
| DROP TYPE IF EXISTS status; | ||||
| DROP TYPE IF EXISTS loglevel; | ||||
| DROP TYPE IF EXISTS log_level; | ||||
| 
 | ||||
| CREATE TYPE status as ENUM ( | ||||
|   'new', | ||||
|   'failed', | ||||
|   'closed' | ||||
|   'closed', | ||||
|   'timeout' | ||||
|   ); | ||||
| 
 | ||||
| CREATE TYPE loglevel as ENUM ( | ||||
| CREATE TYPE log_level as ENUM ( | ||||
|   'fatal', 'panic', 'error', 'warning', 'info', 'debug', 'trace' | ||||
|   ); | ||||
| 
 | ||||
| CREATE TABLE workerIdentity | ||||
| CREATE TABLE worker_identity | ||||
| ( | ||||
|   id          SERIAL PRIMARY KEY, | ||||
|   remote_addr TEXT, | ||||
| @ -24,6 +26,7 @@ CREATE TABLE workerIdentity | ||||
| CREATE TABLE worker | ||||
| ( | ||||
|   id       TEXT PRIMARY KEY, | ||||
|   alias    TEXT DEFAULT NULL, | ||||
|   created  INTEGER, | ||||
|   identity INTEGER REFERENCES workerIdentity (id) | ||||
| ); | ||||
| @ -35,24 +38,35 @@ CREATE TABLE project | ||||
|   name      TEXT UNIQUE, | ||||
|   clone_url TEXT, | ||||
|   git_repo  TEXT UNIQUE, | ||||
|   version   TEXT | ||||
|   version   TEXT, | ||||
|   motd      TEXT, | ||||
|   public    boolean | ||||
| ); | ||||
| 
 | ||||
| CREATE TABLE worker_has_access_to_project | ||||
| ( | ||||
|   worker  TEXT REFERENCES worker (id), | ||||
|   project INTEGER REFERENCES project (id), | ||||
|   primary key (worker, project) | ||||
| ); | ||||
| 
 | ||||
| CREATE TABLE task | ||||
| ( | ||||
|   id          SERIAL PRIMARY KEY, | ||||
|   priority    INTEGER DEFAULT 0, | ||||
|   project     INTEGER REFERENCES project (id), | ||||
|   assignee    TEXT REFERENCES worker (id), | ||||
|   retries     INTEGER DEFAULT 0, | ||||
|   max_retries INTEGER, | ||||
|   status      Status  DEFAULT 'new', | ||||
|   recipe      TEXT | ||||
|   id              SERIAL PRIMARY KEY, | ||||
|   priority        INTEGER DEFAULT 0, | ||||
|   project         INTEGER REFERENCES project (id), | ||||
|   assignee        TEXT REFERENCES worker (id), | ||||
|   retries         INTEGER DEFAULT 0, | ||||
|   max_retries     INTEGER, | ||||
|   status          Status  DEFAULT 'new', | ||||
|   recipe          TEXT, | ||||
|   max_assign_time INTEGER DEFAULT 0, | ||||
|   assign_time     INTEGER DEFAULT 0 | ||||
| ); | ||||
| 
 | ||||
| CREATE TABLE log_entry | ||||
| ( | ||||
|   level        loglevel, | ||||
|   level        log_level, | ||||
|   message      TEXT, | ||||
|   message_data TEXT, | ||||
|   timestamp    INT | ||||
|  | ||||
| @ -15,6 +15,7 @@ type Project struct { | ||||
| 	GitRepo  string `json:"git_repo"` | ||||
| 	Version  string `json:"version"` | ||||
| 	Motd     string `json:"motd"` | ||||
| 	Public   bool   `json:"public"` | ||||
| } | ||||
| 
 | ||||
| type AssignedTasks struct { | ||||
| @ -39,9 +40,9 @@ func (database *Database) SaveProject(project *Project) (int64, error) { | ||||
| 
 | ||||
| func saveProject(project *Project, db *sql.DB) (int64, error) { | ||||
| 
 | ||||
| 	row := db.QueryRow(`INSERT INTO project (name, git_repo, clone_url, version, priority, motd) | ||||
| 		VALUES ($1,$2,$3,$4,$5,$6) RETURNING id`, | ||||
| 		project.Name, project.GitRepo, project.CloneUrl, project.Version, project.Priority, project.Motd) | ||||
| 	row := db.QueryRow(`INSERT INTO project (name, git_repo, clone_url, version, priority, motd, public) | ||||
| 		VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING id`, | ||||
| 		project.Name, project.GitRepo, project.CloneUrl, project.Version, project.Priority, project.Motd, project.Public) | ||||
| 
 | ||||
| 	var id int64 | ||||
| 	err := row.Scan(&id) | ||||
| @ -93,8 +94,8 @@ func getProject(id int64, db *sql.DB) *Project { | ||||
| func scanProject(row *sql.Row) (*Project, error) { | ||||
| 
 | ||||
| 	project := &Project{} | ||||
| 	err := row.Scan(&project.Id, &project.Priority, &project.Motd, &project.Name, &project.CloneUrl, | ||||
| 		&project.GitRepo, &project.Version) | ||||
| 	err := row.Scan(&project.Id, &project.Priority, &project.Name, &project.CloneUrl, | ||||
| 		&project.GitRepo, &project.Version, &project.Motd, &project.Public) | ||||
| 
 | ||||
| 	return project, err | ||||
| } | ||||
| @ -120,8 +121,8 @@ func (database *Database) UpdateProject(project *Project) { | ||||
| 	db := database.getDB() | ||||
| 
 | ||||
| 	res, err := db.Exec(`UPDATE project  | ||||
| 		SET (priority, name, clone_url, git_repo, version, motd) = ($1,$2,$3,$4,$5,$6) WHERE id=$7`, | ||||
| 		project.Priority, project.Name, project.CloneUrl, project.GitRepo, project.Version, project.Motd, project.Id) | ||||
| 		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) | ||||
| 	handleErr(err) | ||||
| 
 | ||||
| 	rowsAffected, _ := res.RowsAffected() | ||||
| @ -156,6 +157,7 @@ func (database *Database) GetProjectStats(id int64) *ProjectStats { | ||||
| 			return nil | ||||
| 		} | ||||
| 
 | ||||
| 		//todo: only expose worker alias | ||||
| 		rows, err := db.Query(`SELECT assignee, COUNT(*) FROM TASK | ||||
|   			LEFT JOIN worker ON TASK.assignee = worker.id WHERE project=$1 GROUP BY assignee`, id) | ||||
| 
 | ||||
| @ -189,7 +191,7 @@ func (database Database) GetAllProjectsStats() *[]ProjectStats { | ||||
| 		stats := ProjectStats{} | ||||
| 		p := &Project{} | ||||
| 		err := rows.Scan(&stats.NewTaskCount, &stats.FailedTaskCount, &stats.ClosedTaskCount, | ||||
| 			&p.Id, &p.Priority, &p.Motd, &p.Name, &p.CloneUrl, &p.GitRepo, &p.Version) | ||||
| 			&p.Id, &p.Priority, &p.Motd, &p.Name, &p.CloneUrl, &p.GitRepo, &p.Version, &p.Public) | ||||
| 		handleErr(err) | ||||
| 
 | ||||
| 		stats.Project = p | ||||
|  | ||||
| @ -7,14 +7,16 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| type Task struct { | ||||
| 	Id         int64     `json:"id"` | ||||
| 	Priority   int64     `json:"priority"` | ||||
| 	Project    *Project  `json:"project"` | ||||
| 	Assignee   uuid.UUID `json:"assignee"` | ||||
| 	Retries    int64     `json:"retries"` | ||||
| 	MaxRetries int64     `json:"max_retries"` | ||||
| 	Status     string    `json:"status"` | ||||
| 	Recipe     string    `json:"recipe"` | ||||
| 	Id            int64     `json:"id"` | ||||
| 	Priority      int64     `json:"priority"` | ||||
| 	Project       *Project  `json:"project"` | ||||
| 	Assignee      uuid.UUID `json:"assignee"` | ||||
| 	Retries       int64     `json:"retries"` | ||||
| 	MaxRetries    int64     `json:"max_retries"` | ||||
| 	Status        string    `json:"status"` | ||||
| 	Recipe        string    `json:"recipe"` | ||||
| 	MaxAssignTime int64     `json:"max_assign_time"` | ||||
| 	AssignTime    int64     `json:"assign_time"` | ||||
| } | ||||
| 
 | ||||
| func (database *Database) SaveTask(task *Task, project int64) error { | ||||
| @ -22,9 +24,9 @@ func (database *Database) SaveTask(task *Task, project int64) error { | ||||
| 	db := database.getDB() | ||||
| 
 | ||||
| 	res, err := db.Exec(` | ||||
| 	INSERT INTO task (project, max_retries, recipe, priority)  | ||||
| 	VALUES ($1,$2,$3,$4)`, | ||||
| 		project, task.MaxRetries, task.Recipe, task.Priority) | ||||
| 	INSERT INTO task (project, max_retries, recipe, priority, max_assign_time)  | ||||
| 	VALUES ($1,$2,$3,$4,$5)`, | ||||
| 		project, task.MaxRetries, task.Recipe, task.Priority, task.MaxAssignTime) | ||||
| 	if err != nil { | ||||
| 		logrus.WithError(err).WithFields(logrus.Fields{ | ||||
| 			"task": task, | ||||
| @ -56,6 +58,9 @@ func (database *Database) GetTask(worker *Worker) *Task { | ||||
| 	FROM task | ||||
| 	INNER JOIN project p on task.project = p.id | ||||
| 	WHERE assignee IS NULL | ||||
| 		AND (p.public OR EXISTS ( | ||||
| 		  SELECT 1 FROM worker_has_access_to_project a WHERE a.worker=$1 AND a.project=p.id | ||||
| 		)) | ||||
| 	ORDER BY p.priority DESC, task.priority DESC | ||||
| 	LIMIT 1 | ||||
| 	) | ||||
| @ -134,6 +139,9 @@ func (database *Database) GetTaskFromProject(worker *Worker, projectId int64) *T | ||||
| 	FROM task | ||||
| 	INNER JOIN project p on task.project = p.id | ||||
| 	WHERE assignee IS NULL AND p.id=$2 | ||||
| 		AND (p.public OR EXISTS ( | ||||
| 		  SELECT 1 FROM worker_has_access_to_project a WHERE a.worker=$1 AND a.project=$2 | ||||
| 		)) | ||||
| 	ORDER BY task.priority DESC | ||||
| 	LIMIT 1 | ||||
| 	) | ||||
| @ -165,8 +173,9 @@ func scanTask(row *sql.Row) *Task { | ||||
| 	task.Project = project | ||||
| 
 | ||||
| 	err := row.Scan(&task.Id, &task.Priority, &project.Id, &task.Assignee, | ||||
| 		&task.Retries, &task.MaxRetries, &task.Status, &task.Recipe, &project.Id, | ||||
| 		&project.Priority, &project.Motd, &project.Name, &project.CloneUrl, &project.GitRepo, &project.Version) | ||||
| 		&task.Retries, &task.MaxRetries, &task.Status, &task.Recipe, &task.MaxAssignTime, | ||||
| 		&task.AssignTime, &project.Id, &project.Priority, &project.Name, | ||||
| 		&project.CloneUrl, &project.GitRepo, &project.Version, &project.Motd, &project.Public) | ||||
| 	handleErr(err) | ||||
| 
 | ||||
| 	return task | ||||
|  | ||||
| @ -101,3 +101,46 @@ func getOrCreateIdentity(identity *Identity, db *sql.DB) int64 { | ||||
| 
 | ||||
| 	return rowId | ||||
| } | ||||
| 
 | ||||
| func (database *Database) GrantAccess(workerId *uuid.UUID, projectId int64) bool { | ||||
| 
 | ||||
| 	db := database.getDB() | ||||
| 	res, err := db.Exec(`INSERT INTO worker_has_access_to_project (worker, project) VALUES ($1,$2) | ||||
| 		ON CONFLICT DO NOTHING`, | ||||
| 		workerId, projectId) | ||||
| 	if err != nil { | ||||
| 		logrus.WithFields(logrus.Fields{ | ||||
| 			"workerId":  workerId, | ||||
| 			"projectId": projectId, | ||||
| 		}).WithError(err).Warn("Database.GrantAccess INSERT worker_hase_access_to_project") | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	rowsAffected, _ := res.RowsAffected() | ||||
| 
 | ||||
| 	logrus.WithFields(logrus.Fields{ | ||||
| 		"rowsAffected": rowsAffected, | ||||
| 		"workerId":     workerId, | ||||
| 		"projectId":    projectId, | ||||
| 	}).Trace("Database.GrantAccess INSERT worker_has_access_to_project") | ||||
| 
 | ||||
| 	return rowsAffected == 1 | ||||
| } | ||||
| 
 | ||||
| func (database Database) RemoveAccess(workerId *uuid.UUID, projectId int64) bool { | ||||
| 
 | ||||
| 	db := database.getDB() | ||||
| 	res, err := db.Exec(`DELETE FROM worker_has_access_to_project WHERE worker=$1 AND project=$2`, | ||||
| 		workerId, projectId) | ||||
| 	handleErr(err) | ||||
| 
 | ||||
| 	rowsAffected, _ := res.RowsAffected() | ||||
| 
 | ||||
| 	logrus.WithFields(logrus.Fields{ | ||||
| 		"rowsAffected": rowsAffected, | ||||
| 		"workerId":     workerId, | ||||
| 		"projectId":    projectId, | ||||
| 	}).Trace("Database.RemoveAccess DELETE worker_has_access_to_project") | ||||
| 
 | ||||
| 	return rowsAffected == 1 | ||||
| } | ||||
|  | ||||
| @ -19,6 +19,7 @@ func TestCreateGetProject(t *testing.T) { | ||||
| 		Version:  "Test Version", | ||||
| 		Priority: 123, | ||||
| 		Motd:     "motd", | ||||
| 		Public:   true, | ||||
| 	}) | ||||
| 
 | ||||
| 	id := resp.Id | ||||
| @ -56,6 +57,9 @@ func TestCreateGetProject(t *testing.T) { | ||||
| 	if getResp.Project.Motd != "motd" { | ||||
| 		t.Error() | ||||
| 	} | ||||
| 	if getResp.Project.Public != true { | ||||
| 		t.Error() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestCreateProjectInvalid(t *testing.T) { | ||||
|  | ||||
| @ -129,6 +129,7 @@ func TestCreateGetTask(t *testing.T) { | ||||
| 		CloneUrl: "http://github.com/test/test", | ||||
| 		GitRepo:  "myrepo", | ||||
| 		Priority: 999, | ||||
| 		Public:   true, | ||||
| 	}) | ||||
| 
 | ||||
| 	createTask(api.CreateTaskRequest{ | ||||
| @ -170,6 +171,9 @@ func TestCreateGetTask(t *testing.T) { | ||||
| 	if taskResp.Task.Project.CloneUrl != "http://github.com/test/test" { | ||||
| 		t.Error() | ||||
| 	} | ||||
| 	if taskResp.Task.Project.Public != true { | ||||
| 		t.Error() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func createTasks(prefix string) (int64, int64) { | ||||
| @ -180,6 +184,7 @@ func createTasks(prefix string) (int64, int64) { | ||||
| 		CloneUrl: "http://github.com/test/test", | ||||
| 		GitRepo:  prefix + "low1", | ||||
| 		Priority: 1, | ||||
| 		Public:   true, | ||||
| 	}) | ||||
| 	highP := createProject(api.CreateProjectRequest{ | ||||
| 		Name:     prefix + "high", | ||||
| @ -187,6 +192,7 @@ func createTasks(prefix string) (int64, int64) { | ||||
| 		CloneUrl: "http://github.com/test/test", | ||||
| 		GitRepo:  prefix + "high1", | ||||
| 		Priority: 999, | ||||
| 		Public:   true, | ||||
| 	}) | ||||
| 	createTask(api.CreateTaskRequest{ | ||||
| 		Project:  lowP.Id, | ||||
| @ -266,6 +272,86 @@ func TestTaskPriority(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestTaskNoAccess(t *testing.T) { | ||||
| 
 | ||||
| 	wid := genWid() | ||||
| 
 | ||||
| 	pid := createProject(api.CreateProjectRequest{ | ||||
| 		Name:     "This is a private proj", | ||||
| 		Motd:     "private", | ||||
| 		Version:  "private", | ||||
| 		Priority: 1, | ||||
| 		CloneUrl: "fjkslejf cesl", | ||||
| 		GitRepo:  "fffffffff", | ||||
| 		Public:   false, | ||||
| 	}).Id | ||||
| 
 | ||||
| 	createResp := createTask(api.CreateTaskRequest{ | ||||
| 		Project:       pid, | ||||
| 		Priority:      1, | ||||
| 		MaxAssignTime: 10, | ||||
| 		MaxRetries:    2, | ||||
| 		Recipe:        "---", | ||||
| 	}) | ||||
| 
 | ||||
| 	if createResp.Ok != true { | ||||
| 		t.Error() | ||||
| 	} | ||||
| 
 | ||||
| 	grantAccess(wid, pid) | ||||
| 	removeAccess(wid, pid) | ||||
| 
 | ||||
| 	tResp := getTaskFromProject(pid, wid) | ||||
| 
 | ||||
| 	if tResp.Ok != false { | ||||
| 		t.Error() | ||||
| 	} | ||||
| 	if len(tResp.Message) <= 0 { | ||||
| 		t.Error() | ||||
| 	} | ||||
| 	if tResp.Task != nil { | ||||
| 		t.Error() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestTaskHasAccess(t *testing.T) { | ||||
| 
 | ||||
| 	wid := genWid() | ||||
| 
 | ||||
| 	pid := createProject(api.CreateProjectRequest{ | ||||
| 		Name:     "This is a private proj1", | ||||
| 		Motd:     "private1", | ||||
| 		Version:  "private1", | ||||
| 		Priority: 1, | ||||
| 		CloneUrl: "josaeiuf cesl", | ||||
| 		GitRepo:  "wewwwwwwwwwwwwwwwwwwwwww", | ||||
| 		Public:   false, | ||||
| 	}).Id | ||||
| 
 | ||||
| 	createResp := createTask(api.CreateTaskRequest{ | ||||
| 		Project:       pid, | ||||
| 		Priority:      1, | ||||
| 		MaxAssignTime: 10, | ||||
| 		MaxRetries:    2, | ||||
| 		Recipe:        "---", | ||||
| 	}) | ||||
| 
 | ||||
| 	if createResp.Ok != true { | ||||
| 		t.Error() | ||||
| 	} | ||||
| 
 | ||||
| 	grantAccess(wid, pid) | ||||
| 
 | ||||
| 	tResp := getTaskFromProject(pid, wid) | ||||
| 
 | ||||
| 	if tResp.Ok != true { | ||||
| 		t.Error() | ||||
| 	} | ||||
| 	if tResp.Task == nil { | ||||
| 		t.Error() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestNoMoreTasks(t *testing.T) { | ||||
| 
 | ||||
| 	wid := genWid() | ||||
|  | ||||
| @ -66,6 +66,78 @@ func TestGetWorkerInvalid(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestGrantAccessFailedProjectConstraint(t *testing.T) { | ||||
| 
 | ||||
| 	wid := genWid() | ||||
| 
 | ||||
| 	resp := grantAccess(wid, 38274593) | ||||
| 
 | ||||
| 	if resp.Ok != false { | ||||
| 		t.Error() | ||||
| 	} | ||||
| 	if len(resp.Message) <= 0 { | ||||
| 		t.Error() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestRemoveAccessFailedProjectConstraint(t *testing.T) { | ||||
| 
 | ||||
| 	wid := genWid() | ||||
| 
 | ||||
| 	resp := removeAccess(wid, 38274593) | ||||
| 
 | ||||
| 	if resp.Ok != false { | ||||
| 		t.Error() | ||||
| 	} | ||||
| 	if len(resp.Message) <= 0 { | ||||
| 		t.Error() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestRemoveAccessFailedWorkerConstraint(t *testing.T) { | ||||
| 
 | ||||
| 	pid := createProject(api.CreateProjectRequest{ | ||||
| 		Priority: 1, | ||||
| 		GitRepo:  "dfffffffffff", | ||||
| 		CloneUrl: "fffffffffff23r", | ||||
| 		Version:  "f83w9rw", | ||||
| 		Motd:     "ddddddddd", | ||||
| 		Name:     "removeaccessfailedworkerconstraint", | ||||
| 		Public:   true, | ||||
| 	}).Id | ||||
| 
 | ||||
| 	resp := removeAccess(&uuid.Nil, pid) | ||||
| 
 | ||||
| 	if resp.Ok != false { | ||||
| 		t.Error() | ||||
| 	} | ||||
| 	if len(resp.Message) <= 0 { | ||||
| 		t.Error() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestGrantAccessFailedWorkerConstraint(t *testing.T) { | ||||
| 
 | ||||
| 	pid := createProject(api.CreateProjectRequest{ | ||||
| 		Priority: 1, | ||||
| 		GitRepo:  "dfffffffffff1", | ||||
| 		CloneUrl: "fffffffffff23r1", | ||||
| 		Version:  "f83w9rw1", | ||||
| 		Motd:     "ddddddddd1", | ||||
| 		Name:     "grantaccessfailedworkerconstraint", | ||||
| 		Public:   true, | ||||
| 	}).Id | ||||
| 
 | ||||
| 	resp := removeAccess(&uuid.Nil, pid) | ||||
| 
 | ||||
| 	if resp.Ok != false { | ||||
| 		t.Error() | ||||
| 	} | ||||
| 	if len(resp.Message) <= 0 { | ||||
| 		t.Error() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func createWorker(req api.CreateWorkerRequest) (*api.CreateWorkerResponse, *http.Response) { | ||||
| 	r := Post("/worker/create", req) | ||||
| 
 | ||||
| @ -94,3 +166,33 @@ func genWid() *uuid.UUID { | ||||
| 	resp, _ := createWorker(api.CreateWorkerRequest{}) | ||||
| 	return &resp.WorkerId | ||||
| } | ||||
| 
 | ||||
| func grantAccess(wid *uuid.UUID, project int64) *api.WorkerAccessResponse { | ||||
| 
 | ||||
| 	r := Post("/access/grant", api.WorkerAccessRequest{ | ||||
| 		WorkerId:  wid, | ||||
| 		ProjectId: project, | ||||
| 	}) | ||||
| 
 | ||||
| 	var resp *api.WorkerAccessResponse | ||||
| 	data, _ := ioutil.ReadAll(r.Body) | ||||
| 	err := json.Unmarshal(data, &resp) | ||||
| 	handleErr(err) | ||||
| 
 | ||||
| 	return resp | ||||
| } | ||||
| 
 | ||||
| func removeAccess(wid *uuid.UUID, project int64) *api.WorkerAccessResponse { | ||||
| 
 | ||||
| 	r := Post("/access/remove", api.WorkerAccessRequest{ | ||||
| 		WorkerId:  wid, | ||||
| 		ProjectId: project, | ||||
| 	}) | ||||
| 
 | ||||
| 	var resp *api.WorkerAccessResponse | ||||
| 	data, _ := ioutil.ReadAll(r.Body) | ||||
| 	err := json.Unmarshal(data, &resp) | ||||
| 	handleErr(err) | ||||
| 
 | ||||
| 	return resp | ||||
| } | ||||
|  | ||||
							
								
								
									
										45
									
								
								test/schema.sql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										45
									
								
								test/schema.sql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -1,18 +1,20 @@ | ||||
| DROP TABLE IF EXISTS workeridentity, Worker, Project, Task, log_entry; | ||||
| DROP TABLE IF EXISTS worker_identity, worker, project, task, log_entry, | ||||
|   worker_has_access_to_project; | ||||
| DROP TYPE IF EXISTS status; | ||||
| DROP TYPE IF EXISTS loglevel; | ||||
| DROP TYPE IF EXISTS log_level; | ||||
| 
 | ||||
| CREATE TYPE status as ENUM ( | ||||
|   'new', | ||||
|   'failed', | ||||
|   'closed' | ||||
|   'closed', | ||||
|   'timeout' | ||||
|   ); | ||||
| 
 | ||||
| CREATE TYPE loglevel as ENUM ( | ||||
| CREATE TYPE log_level as ENUM ( | ||||
|   'fatal', 'panic', 'error', 'warning', 'info', 'debug', 'trace' | ||||
|   ); | ||||
| 
 | ||||
| CREATE TABLE workerIdentity | ||||
| CREATE TABLE worker_identity | ||||
| ( | ||||
|   id          SERIAL PRIMARY KEY, | ||||
|   remote_addr TEXT, | ||||
| @ -24,6 +26,7 @@ CREATE TABLE workerIdentity | ||||
| CREATE TABLE worker | ||||
| ( | ||||
|   id       TEXT PRIMARY KEY, | ||||
|   alias    TEXT DEFAULT NULL, | ||||
|   created  INTEGER, | ||||
|   identity INTEGER REFERENCES workerIdentity (id) | ||||
| ); | ||||
| @ -32,28 +35,38 @@ CREATE TABLE project | ||||
| ( | ||||
|   id        SERIAL PRIMARY KEY, | ||||
|   priority  INTEGER DEFAULT 0, | ||||
|   motd      TEXT    DEFAULT '', | ||||
|   name      TEXT UNIQUE, | ||||
|   clone_url TEXT, | ||||
|   git_repo  TEXT UNIQUE, | ||||
|   version   TEXT | ||||
|   version   TEXT, | ||||
|   motd      TEXT, | ||||
|   public    boolean | ||||
| ); | ||||
| 
 | ||||
| CREATE TABLE worker_has_access_to_project | ||||
| ( | ||||
|   worker  TEXT REFERENCES worker (id), | ||||
|   project INTEGER REFERENCES project (id), | ||||
|   primary key (worker, project) | ||||
| ); | ||||
| 
 | ||||
| CREATE TABLE task | ||||
| ( | ||||
|   id          SERIAL PRIMARY KEY, | ||||
|   priority    INTEGER DEFAULT 0, | ||||
|   project     INTEGER REFERENCES project (id), | ||||
|   assignee    TEXT REFERENCES worker (id), | ||||
|   retries     INTEGER DEFAULT 0, | ||||
|   max_retries INTEGER, | ||||
|   status      Status  DEFAULT 'new', | ||||
|   recipe      TEXT | ||||
|   id              SERIAL PRIMARY KEY, | ||||
|   priority        INTEGER DEFAULT 0, | ||||
|   project         INTEGER REFERENCES project (id), | ||||
|   assignee        TEXT REFERENCES worker (id), | ||||
|   retries         INTEGER DEFAULT 0, | ||||
|   max_retries     INTEGER, | ||||
|   status          Status  DEFAULT 'new', | ||||
|   recipe          TEXT, | ||||
|   max_assign_time INTEGER DEFAULT 0, | ||||
|   assign_time     INTEGER DEFAULT 0 | ||||
| ); | ||||
| 
 | ||||
| CREATE TABLE log_entry | ||||
| ( | ||||
|   level        loglevel, | ||||
|   level        log_level, | ||||
|   message      TEXT, | ||||
|   message_data TEXT, | ||||
|   timestamp    INT | ||||
|  | ||||
| @ -10,12 +10,16 @@ import { | ||||
|     MatAutocompleteModule, | ||||
|     MatButtonModule, | ||||
|     MatCardModule, | ||||
|     MatCheckboxModule, | ||||
|     MatDividerModule, | ||||
|     MatExpansionModule, | ||||
|     MatFormFieldModule, | ||||
|     MatIconModule, | ||||
|     MatInputModule, | ||||
|     MatMenuModule, | ||||
|     MatPaginatorModule, | ||||
|     MatSliderModule, | ||||
|     MatSlideToggleModule, | ||||
|     MatSortModule, | ||||
|     MatTableModule, | ||||
|     MatToolbarModule, | ||||
| @ -58,6 +62,11 @@ import {UpdateProjectComponent} from './update-project/update-project.component' | ||||
|         MatTreeModule, | ||||
|         BrowserAnimationsModule, | ||||
|         HttpClientModule, | ||||
|         MatSliderModule, | ||||
|         MatSlideToggleModule, | ||||
|         MatCheckboxModule, | ||||
|         MatDividerModule | ||||
| 
 | ||||
|     ], | ||||
|     exports: [], | ||||
|     providers: [ | ||||
|  | ||||
| @ -4,21 +4,30 @@ | ||||
| 
 | ||||
|     <mat-card-content> | ||||
|         <form> | ||||
|             <mat-form-field> | ||||
|                 <input type="text" matInput [(ngModel)]="project.name" name="name" placeholder="Name"> | ||||
|             <mat-form-field appearance="outline"> | ||||
|                 <mat-label>Project name</mat-label> | ||||
|                 <input type="text" matInput [(ngModel)]="project.name" name="name" placeholder="Project name"> | ||||
|             </mat-form-field> | ||||
| 
 | ||||
|             <mat-form-field> | ||||
| 
 | ||||
|             <mat-form-field appearance="outline"> | ||||
|                 <mat-label>Git clone URL</mat-label> | ||||
|                 <input type="text" matInput [(ngModel)]="project.clone_url" name="clone_url" | ||||
|                        placeholder="Git clone url"> | ||||
|             </mat-form-field> | ||||
|             <mat-form-field> | ||||
|             <mat-form-field appearance="outline"> | ||||
|                 <mat-label>Repository name</mat-label> | ||||
|                 <input type="text" matInput [(ngModel)]="project.git_repo" name="clone_url" | ||||
|                        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 | ||||
|                     enabled | ||||
|                 <mat-hint align="start"> | ||||
|                     Changes on the <strong>master</strong> branch will be tracked if webhooks are enabled | ||||
|                 </mat-hint> | ||||
|             </mat-form-field> | ||||
| 
 | ||||
|             <mat-checkbox matInput [(ngModel)]="project.public" name="public" stype="padding-top: 1em">Public project | ||||
|             </mat-checkbox> | ||||
| 
 | ||||
| 
 | ||||
|             <input type="hidden" name="version" value="{{project.version}}"> | ||||
|         </form> | ||||
|     </mat-card-content> | ||||
|  | ||||
| @ -11,7 +11,8 @@ export class CreateProjectComponent implements OnInit { | ||||
|     private project = new Project(); | ||||
| 
 | ||||
|     constructor() { | ||||
|         this.project.name = "test" | ||||
|         this.project.name = "test"; | ||||
|         this.project.public = true; | ||||
|     } | ||||
| 
 | ||||
|     ngOnInit() { | ||||
|  | ||||
| @ -6,4 +6,5 @@ export class Project { | ||||
|     public clone_url: string; | ||||
|     public git_repo: string; | ||||
|     public version: string; | ||||
|     public public: boolean; | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user