Added public project attribute & worker access api endpoints

This commit is contained in:
simon987 2019-01-24 20:39:17 -05:00
parent 1d656099f5
commit f250a2180c
16 changed files with 432 additions and 70 deletions

View File

@ -50,6 +50,9 @@ func New() *WebAPI {
api.router.POST("/worker/create", LogRequestMiddleware(api.WorkerCreate)) api.router.POST("/worker/create", LogRequestMiddleware(api.WorkerCreate))
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/remove", LogRequestMiddleware(api.WorkerRemoveAccess))
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.GET("/project/stats/:id", LogRequestMiddleware(api.ProjectGetStats)) api.router.GET("/project/stats/:id", LogRequestMiddleware(api.ProjectGetStats))

View File

@ -13,6 +13,7 @@ type CreateProjectRequest struct {
Version string `json:"version"` Version string `json:"version"`
Priority int64 `json:"priority"` Priority int64 `json:"priority"`
Motd string `json:"motd"` Motd string `json:"motd"`
Public bool `json:"public"`
} }
type CreateProjectResponse struct { type CreateProjectResponse struct {
@ -51,6 +52,7 @@ func (api *WebAPI) ProjectCreate(r *Request) {
GitRepo: createReq.GitRepo, GitRepo: createReq.GitRepo,
Priority: createReq.Priority, Priority: createReq.Priority,
Motd: createReq.Motd, Motd: createReq.Motd,
Public: createReq.Public,
} }
if isValidProject(project) { if isValidProject(project) {

View File

@ -9,10 +9,11 @@ import (
) )
type CreateTaskRequest struct { type CreateTaskRequest struct {
Project int64 `json:"project"` Project int64 `json:"project"`
MaxRetries int64 `json:"max_retries"` MaxRetries int64 `json:"max_retries"`
Recipe string `json:"recipe"` Recipe string `json:"recipe"`
Priority int64 `json:"priority"` Priority int64 `json:"priority"`
MaxAssignTime int64 `json:"max_assign_time"`
} }
type ReleaseTaskRequest struct { type ReleaseTaskRequest struct {
@ -43,9 +44,11 @@ func (api *WebAPI) TaskCreate(r *Request) {
if r.GetJson(&createReq) { if r.GetJson(&createReq) {
task := &storage.Task{ task := &storage.Task{
MaxRetries: createReq.MaxRetries, MaxRetries: createReq.MaxRetries,
Recipe: createReq.Recipe, Recipe: createReq.Recipe,
Priority: createReq.Priority, Priority: createReq.Priority,
AssignTime: 0,
MaxAssignTime: createReq.MaxAssignTime,
} }
if isTaskValid(task) { if isTaskValid(task) {
@ -99,10 +102,21 @@ func (api *WebAPI) TaskGetFromProject(r *Request) {
handleErr(err, r) handleErr(err, r)
task := api.Database.GetTaskFromProject(worker, int64(project)) task := api.Database.GetTaskFromProject(worker, int64(project))
r.OkJson(GetTaskResponse{ if task == nil {
Ok: true,
Task: task, r.OkJson(GetTaskResponse{
}) Ok: false,
Message: "No task available",
})
} else {
r.OkJson(GetTaskResponse{
Ok: true,
Task: task,
})
}
} }
func (api *WebAPI) TaskGet(r *Request) { func (api *WebAPI) TaskGet(r *Request) {

View File

@ -22,6 +22,16 @@ type GetWorkerResponse struct {
Worker *storage.Worker `json:"worker,omitempty"` 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) { func (api *WebAPI) WorkerCreate(r *Request) {
workerReq := &CreateWorkerRequest{} 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) { func (api *WebAPI) workerCreate(request *CreateWorkerRequest, identity *storage.Identity) (uuid.UUID, error) {
worker := storage.Worker{ worker := storage.Worker{

View 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 status;
DROP TYPE IF EXISTS loglevel; DROP TYPE IF EXISTS log_level;
CREATE TYPE status as ENUM ( CREATE TYPE status as ENUM (
'new', 'new',
'failed', 'failed',
'closed' 'closed',
'timeout'
); );
CREATE TYPE loglevel as ENUM ( CREATE TYPE log_level as ENUM (
'fatal', 'panic', 'error', 'warning', 'info', 'debug', 'trace' 'fatal', 'panic', 'error', 'warning', 'info', 'debug', 'trace'
); );
CREATE TABLE workerIdentity CREATE TABLE worker_identity
( (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
remote_addr TEXT, remote_addr TEXT,
@ -24,6 +26,7 @@ CREATE TABLE workerIdentity
CREATE TABLE worker CREATE TABLE worker
( (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
alias TEXT DEFAULT NULL,
created INTEGER, created INTEGER,
identity INTEGER REFERENCES workerIdentity (id) identity INTEGER REFERENCES workerIdentity (id)
); );
@ -35,24 +38,35 @@ CREATE TABLE project
name TEXT UNIQUE, name TEXT UNIQUE,
clone_url TEXT, clone_url TEXT,
git_repo TEXT UNIQUE, 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 CREATE TABLE task
( (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
priority INTEGER DEFAULT 0, priority INTEGER DEFAULT 0,
project INTEGER REFERENCES project (id), project INTEGER REFERENCES project (id),
assignee TEXT REFERENCES worker (id), assignee TEXT REFERENCES worker (id),
retries INTEGER DEFAULT 0, retries INTEGER DEFAULT 0,
max_retries INTEGER, max_retries INTEGER,
status Status DEFAULT 'new', status Status DEFAULT 'new',
recipe TEXT recipe TEXT,
max_assign_time INTEGER DEFAULT 0,
assign_time INTEGER DEFAULT 0
); );
CREATE TABLE log_entry CREATE TABLE log_entry
( (
level loglevel, level log_level,
message TEXT, message TEXT,
message_data TEXT, message_data TEXT,
timestamp INT timestamp INT

View File

@ -15,6 +15,7 @@ type Project struct {
GitRepo string `json:"git_repo"` GitRepo string `json:"git_repo"`
Version string `json:"version"` Version string `json:"version"`
Motd string `json:"motd"` Motd string `json:"motd"`
Public bool `json:"public"`
} }
type AssignedTasks struct { type AssignedTasks struct {
@ -39,9 +40,9 @@ func (database *Database) SaveProject(project *Project) (int64, error) {
func saveProject(project *Project, db *sql.DB) (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) row := db.QueryRow(`INSERT INTO project (name, git_repo, clone_url, version, priority, motd, public)
VALUES ($1,$2,$3,$4,$5,$6) RETURNING id`, VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING id`,
project.Name, project.GitRepo, project.CloneUrl, project.Version, project.Priority, project.Motd) project.Name, project.GitRepo, project.CloneUrl, project.Version, project.Priority, project.Motd, project.Public)
var id int64 var id int64
err := row.Scan(&id) err := row.Scan(&id)
@ -93,8 +94,8 @@ func getProject(id int64, db *sql.DB) *Project {
func scanProject(row *sql.Row) (*Project, error) { func scanProject(row *sql.Row) (*Project, error) {
project := &Project{} project := &Project{}
err := row.Scan(&project.Id, &project.Priority, &project.Motd, &project.Name, &project.CloneUrl, err := row.Scan(&project.Id, &project.Priority, &project.Name, &project.CloneUrl,
&project.GitRepo, &project.Version) &project.GitRepo, &project.Version, &project.Motd, &project.Public)
return project, err return project, err
} }
@ -120,8 +121,8 @@ func (database *Database) UpdateProject(project *Project) {
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) = ($1,$2,$3,$4,$5,$6) WHERE id=$7`, 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.Id) project.Priority, project.Name, project.CloneUrl, project.GitRepo, project.Version, project.Motd, project.Public, project.Id)
handleErr(err) handleErr(err)
rowsAffected, _ := res.RowsAffected() rowsAffected, _ := res.RowsAffected()
@ -156,6 +157,7 @@ func (database *Database) GetProjectStats(id int64) *ProjectStats {
return nil return nil
} }
//todo: only expose worker alias
rows, err := db.Query(`SELECT assignee, COUNT(*) FROM TASK rows, err := db.Query(`SELECT assignee, COUNT(*) FROM TASK
LEFT JOIN worker ON TASK.assignee = worker.id WHERE project=$1 GROUP BY assignee`, id) 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{} 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.Id, &p.Priority, &p.Motd, &p.Name, &p.CloneUrl, &p.GitRepo, &p.Version, &p.Public)
handleErr(err) handleErr(err)
stats.Project = p stats.Project = p

View File

@ -7,14 +7,16 @@ import (
) )
type Task struct { type Task struct {
Id int64 `json:"id"` Id int64 `json:"id"`
Priority int64 `json:"priority"` Priority int64 `json:"priority"`
Project *Project `json:"project"` Project *Project `json:"project"`
Assignee uuid.UUID `json:"assignee"` Assignee uuid.UUID `json:"assignee"`
Retries int64 `json:"retries"` Retries int64 `json:"retries"`
MaxRetries int64 `json:"max_retries"` MaxRetries int64 `json:"max_retries"`
Status string `json:"status"` Status string `json:"status"`
Recipe string `json:"recipe"` Recipe string `json:"recipe"`
MaxAssignTime int64 `json:"max_assign_time"`
AssignTime int64 `json:"assign_time"`
} }
func (database *Database) SaveTask(task *Task, project int64) error { 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() db := database.getDB()
res, err := db.Exec(` res, err := db.Exec(`
INSERT INTO task (project, max_retries, recipe, priority) INSERT INTO task (project, max_retries, recipe, priority, max_assign_time)
VALUES ($1,$2,$3,$4)`, VALUES ($1,$2,$3,$4,$5)`,
project, task.MaxRetries, task.Recipe, task.Priority) 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{
"task": task, "task": task,
@ -56,6 +58,9 @@ func (database *Database) GetTask(worker *Worker) *Task {
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 (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 ORDER BY p.priority DESC, task.priority DESC
LIMIT 1 LIMIT 1
) )
@ -134,6 +139,9 @@ func (database *Database) GetTaskFromProject(worker *Worker, projectId int64) *T
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 (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 ORDER BY task.priority DESC
LIMIT 1 LIMIT 1
) )
@ -165,8 +173,9 @@ func scanTask(row *sql.Row) *Task {
task.Project = project task.Project = project
err := row.Scan(&task.Id, &task.Priority, &project.Id, &task.Assignee, err := row.Scan(&task.Id, &task.Priority, &project.Id, &task.Assignee,
&task.Retries, &task.MaxRetries, &task.Status, &task.Recipe, &project.Id, &task.Retries, &task.MaxRetries, &task.Status, &task.Recipe, &task.MaxAssignTime,
&project.Priority, &project.Motd, &project.Name, &project.CloneUrl, &project.GitRepo, &project.Version) &task.AssignTime, &project.Id, &project.Priority, &project.Name,
&project.CloneUrl, &project.GitRepo, &project.Version, &project.Motd, &project.Public)
handleErr(err) handleErr(err)
return task return task

View File

@ -101,3 +101,46 @@ func getOrCreateIdentity(identity *Identity, db *sql.DB) int64 {
return rowId 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
}

View File

@ -19,6 +19,7 @@ func TestCreateGetProject(t *testing.T) {
Version: "Test Version", Version: "Test Version",
Priority: 123, Priority: 123,
Motd: "motd", Motd: "motd",
Public: true,
}) })
id := resp.Id id := resp.Id
@ -56,6 +57,9 @@ func TestCreateGetProject(t *testing.T) {
if getResp.Project.Motd != "motd" { if getResp.Project.Motd != "motd" {
t.Error() t.Error()
} }
if getResp.Project.Public != true {
t.Error()
}
} }
func TestCreateProjectInvalid(t *testing.T) { func TestCreateProjectInvalid(t *testing.T) {

View File

@ -129,6 +129,7 @@ func TestCreateGetTask(t *testing.T) {
CloneUrl: "http://github.com/test/test", CloneUrl: "http://github.com/test/test",
GitRepo: "myrepo", GitRepo: "myrepo",
Priority: 999, Priority: 999,
Public: true,
}) })
createTask(api.CreateTaskRequest{ createTask(api.CreateTaskRequest{
@ -170,6 +171,9 @@ func TestCreateGetTask(t *testing.T) {
if taskResp.Task.Project.CloneUrl != "http://github.com/test/test" { if taskResp.Task.Project.CloneUrl != "http://github.com/test/test" {
t.Error() t.Error()
} }
if taskResp.Task.Project.Public != true {
t.Error()
}
} }
func createTasks(prefix string) (int64, int64) { func createTasks(prefix string) (int64, int64) {
@ -180,6 +184,7 @@ func createTasks(prefix string) (int64, int64) {
CloneUrl: "http://github.com/test/test", CloneUrl: "http://github.com/test/test",
GitRepo: prefix + "low1", GitRepo: prefix + "low1",
Priority: 1, Priority: 1,
Public: true,
}) })
highP := createProject(api.CreateProjectRequest{ highP := createProject(api.CreateProjectRequest{
Name: prefix + "high", Name: prefix + "high",
@ -187,6 +192,7 @@ func createTasks(prefix string) (int64, int64) {
CloneUrl: "http://github.com/test/test", CloneUrl: "http://github.com/test/test",
GitRepo: prefix + "high1", GitRepo: prefix + "high1",
Priority: 999, Priority: 999,
Public: true,
}) })
createTask(api.CreateTaskRequest{ createTask(api.CreateTaskRequest{
Project: lowP.Id, 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) { func TestNoMoreTasks(t *testing.T) {
wid := genWid() wid := genWid()

View File

@ -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) { func createWorker(req api.CreateWorkerRequest) (*api.CreateWorkerResponse, *http.Response) {
r := Post("/worker/create", req) r := Post("/worker/create", req)
@ -94,3 +166,33 @@ func genWid() *uuid.UUID {
resp, _ := createWorker(api.CreateWorkerRequest{}) resp, _ := createWorker(api.CreateWorkerRequest{})
return &resp.WorkerId 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
View 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 status;
DROP TYPE IF EXISTS loglevel; DROP TYPE IF EXISTS log_level;
CREATE TYPE status as ENUM ( CREATE TYPE status as ENUM (
'new', 'new',
'failed', 'failed',
'closed' 'closed',
'timeout'
); );
CREATE TYPE loglevel as ENUM ( CREATE TYPE log_level as ENUM (
'fatal', 'panic', 'error', 'warning', 'info', 'debug', 'trace' 'fatal', 'panic', 'error', 'warning', 'info', 'debug', 'trace'
); );
CREATE TABLE workerIdentity CREATE TABLE worker_identity
( (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
remote_addr TEXT, remote_addr TEXT,
@ -24,6 +26,7 @@ CREATE TABLE workerIdentity
CREATE TABLE worker CREATE TABLE worker
( (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
alias TEXT DEFAULT NULL,
created INTEGER, created INTEGER,
identity INTEGER REFERENCES workerIdentity (id) identity INTEGER REFERENCES workerIdentity (id)
); );
@ -32,28 +35,38 @@ CREATE TABLE project
( (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
priority INTEGER DEFAULT 0, priority INTEGER DEFAULT 0,
motd TEXT DEFAULT '',
name TEXT UNIQUE, name TEXT UNIQUE,
clone_url TEXT, clone_url TEXT,
git_repo TEXT UNIQUE, 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 CREATE TABLE task
( (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
priority INTEGER DEFAULT 0, priority INTEGER DEFAULT 0,
project INTEGER REFERENCES project (id), project INTEGER REFERENCES project (id),
assignee TEXT REFERENCES worker (id), assignee TEXT REFERENCES worker (id),
retries INTEGER DEFAULT 0, retries INTEGER DEFAULT 0,
max_retries INTEGER, max_retries INTEGER,
status Status DEFAULT 'new', status Status DEFAULT 'new',
recipe TEXT recipe TEXT,
max_assign_time INTEGER DEFAULT 0,
assign_time INTEGER DEFAULT 0
); );
CREATE TABLE log_entry CREATE TABLE log_entry
( (
level loglevel, level log_level,
message TEXT, message TEXT,
message_data TEXT, message_data TEXT,
timestamp INT timestamp INT

View File

@ -10,12 +10,16 @@ import {
MatAutocompleteModule, MatAutocompleteModule,
MatButtonModule, MatButtonModule,
MatCardModule, MatCardModule,
MatCheckboxModule,
MatDividerModule,
MatExpansionModule, MatExpansionModule,
MatFormFieldModule, MatFormFieldModule,
MatIconModule, MatIconModule,
MatInputModule, MatInputModule,
MatMenuModule, MatMenuModule,
MatPaginatorModule, MatPaginatorModule,
MatSliderModule,
MatSlideToggleModule,
MatSortModule, MatSortModule,
MatTableModule, MatTableModule,
MatToolbarModule, MatToolbarModule,
@ -58,6 +62,11 @@ import {UpdateProjectComponent} from './update-project/update-project.component'
MatTreeModule, MatTreeModule,
BrowserAnimationsModule, BrowserAnimationsModule,
HttpClientModule, HttpClientModule,
MatSliderModule,
MatSlideToggleModule,
MatCheckboxModule,
MatDividerModule
], ],
exports: [], exports: [],
providers: [ providers: [

View File

@ -4,21 +4,30 @@
<mat-card-content> <mat-card-content>
<form> <form>
<mat-form-field> <mat-form-field appearance="outline">
<input type="text" matInput [(ngModel)]="project.name" name="name" placeholder="Name"> <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>
<mat-form-field appearance="outline">
<mat-label>Git clone URL</mat-label>
<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">
<mat-label>Repository name</mat-label>
<input type="text" matInput [(ngModel)]="project.git_repo" name="clone_url" <input type="text" matInput [(ngModel)]="project.git_repo" name="clone_url"
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">
enabled Changes on the <strong>master</strong> branch will be tracked if webhooks are enabled
</mat-hint> </mat-hint>
</mat-form-field> </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}}"> <input type="hidden" name="version" value="{{project.version}}">
</form> </form>
</mat-card-content> </mat-card-content>

View File

@ -11,7 +11,8 @@ export class CreateProjectComponent implements OnInit {
private project = new Project(); private project = new Project();
constructor() { constructor() {
this.project.name = "test" this.project.name = "test";
this.project.public = true;
} }
ngOnInit() { ngOnInit() {

View File

@ -6,4 +6,5 @@ export class Project {
public clone_url: string; public clone_url: string;
public git_repo: string; public git_repo: string;
public version: string; public version: string;
public public: boolean;
} }