Initial commit

This commit is contained in:
simon987
2019-01-12 16:18:14 -05:00
commit 83276ce8b0
25 changed files with 1367 additions and 0 deletions

25
api/error.go Normal file
View File

@@ -0,0 +1,25 @@
package api
import (
"github.com/Sirupsen/logrus"
"runtime/debug"
)
type ErrorResponse struct {
Message string `json:"message"`
StackTrace string `json:"stack_trace"`
}
func handleErr(err error, r *Request) {
if err != nil {
logrus.Error(err.Error())
//debug.PrintStack()
r.Json(ErrorResponse{
Message: err.Error(),
StackTrace: string(debug.Stack()),
}, 500)
}
}

48
api/helper.go Normal file
View File

@@ -0,0 +1,48 @@
package api
import (
"encoding/json"
"fmt"
"github.com/Sirupsen/logrus"
"github.com/valyala/fasthttp"
)
type Request struct {
Ctx *fasthttp.RequestCtx
}
func (r *Request) OkJson(object interface{}) {
resp, err := json.Marshal(object)
handleErr(err, r)
r.Ctx.Response.Header.Set("Content-Type", "application/json")
_, err = r.Ctx.Write(resp)
handleErr(err, r)
}
func (r *Request) Json(object interface{}, code int) {
resp, err := json.Marshal(object)
if err != nil {
fmt.Fprint(r.Ctx,"Error during json encoding of error")
logrus.Error("Error during json encoding of error")
}
r.Ctx.Response.SetStatusCode(code)
r.Ctx.Response.Header.Set("Content-Type", "application/json")
_, err = r.Ctx.Write(resp)
if err != nil {
panic(err) //todo handle differently
}
}
func (r *Request) GetJson(x interface{}) bool {
err := json.Unmarshal(r.Ctx.Request.Body(), x)
handleErr(err, r)
return err == nil
}

91
api/log.go Normal file
View File

@@ -0,0 +1,91 @@
package api
import (
"errors"
"github.com/Sirupsen/logrus"
"github.com/valyala/fasthttp"
"time"
)
type RequestHandler func(*Request)
type LogEntry struct {
Scope string `json:"scope"`
Message string `json:"Message"`
TimeStamp int64 `json:"timestamp"`
}
func (e *LogEntry) Time() time.Time {
t := time.Unix(e.TimeStamp, 0)
return t
}
func LogRequest(h RequestHandler) fasthttp.RequestHandler {
return fasthttp.RequestHandler(func(ctx *fasthttp.RequestCtx) {
logrus.WithFields(logrus.Fields{
"path": string(ctx.Path()),
}).Info(string(ctx.Method()))
h(&Request{Ctx: ctx})
})
}
func SetupLogger() {
logrus.SetLevel(logrus.TraceLevel) //todo: from conf
}
func parseLogEntry(r *Request) *LogEntry {
entry := LogEntry{}
if r.GetJson(&entry) {
if len(entry.Message) == 0 {
handleErr(errors.New("invalid message"), r)
} else if len(entry.Scope) == 0 {
handleErr(errors.New("invalid scope"), r)
} else if entry.TimeStamp <= 0 {
handleErr(errors.New("invalid timestamp"), r)
}
}
return &entry
}
func LogTrace(r *Request) {
entry := parseLogEntry(r)
logrus.WithFields(logrus.Fields{
"scope": entry.Scope,
}).WithTime(entry.Time()).Trace(entry.Message)
}
func LogInfo(r *Request) {
entry := parseLogEntry(r)
logrus.WithFields(logrus.Fields{
"scope": entry.Scope,
}).WithTime(entry.Time()).Info(entry.Message)
}
func LogWarn(r *Request) {
entry := parseLogEntry(r)
logrus.WithFields(logrus.Fields{
"scope": entry.Scope,
}).WithTime(entry.Time()).Warn(entry.Message)
}
func LogError(r *Request) {
entry := parseLogEntry(r)
logrus.WithFields(logrus.Fields{
"scope": entry.Scope,
}).WithTime(entry.Time()).Error(entry.Message)
}

71
api/main.go Normal file
View File

@@ -0,0 +1,71 @@
package api
import (
"github.com/Sirupsen/logrus"
"github.com/buaazp/fasthttprouter"
"github.com/valyala/fasthttp"
"src/task_tracker/config"
"src/task_tracker/storage"
)
type WebAPI struct {
server *fasthttp.Server
router *fasthttprouter.Router
Database *storage.Database
}
type Info struct {
Name string `json:"name"`
Version string `json:"version"`
}
var info = Info {
Name: "task_tracker",
Version: "1.0",
}
func Index(r *Request) {
r.OkJson(info)
}
func New() *WebAPI {
SetupLogger()
api := new(WebAPI)
api.router = &fasthttprouter.Router{}
api.server = &fasthttp.Server{
Handler: api.router.Handler,
Name: info.Name,
}
api.router.GET("/", LogRequest(Index))
api.router.POST("/log/trace", LogRequest(LogTrace))
api.router.POST("/log/info", LogRequest(LogInfo))
api.router.POST("/log/warn", LogRequest(LogWarn))
api.router.POST("/log/error", LogRequest(LogError))
api.router.POST("/worker/create", LogRequest(api.WorkerCreate))
api.router.GET("/worker/get/:id", LogRequest(api.WorkerGet))
api.router.POST("/project/create", LogRequest(api.ProjectCreate))
api.router.GET("/project/get/:id", LogRequest(api.ProjectGet))
api.router.POST("/task/create", LogRequest(api.TaskCreate))
api.router.GET("/task/get/", LogRequest(api.TaskGet))
return api
}
func (api *WebAPI) Run() {
logrus.Infof("Started web api at address %s", config.Cfg.ServerAddr)
err := api.server.ListenAndServe(config.Cfg.ServerAddr)
if err != nil {
logrus.Fatalf("Error in ListenAndServe: %s", err)
}
}

92
api/project.go Normal file
View File

@@ -0,0 +1,92 @@
package api
import (
"github.com/Sirupsen/logrus"
"src/task_tracker/storage"
"strconv"
)
type CreateProjectRequest struct {
Name string `json:"name"`
GitUrl string `json:"git_url"`
Version string `json:"version"`
}
type CreateProjectResponse struct {
Ok bool `json:"ok"`
Id int64 `json:"id,omitempty"`
Message string `json:"message,omitempty"`
}
type GetProjectResponse struct {
Ok bool `json:"ok"`
Message string `json:"message,omitempty"`
Project *storage.Project `json:"project,omitempty"`
}
func (api *WebAPI) ProjectCreate(r *Request) {
createReq := &CreateProjectRequest{}
if r.GetJson(createReq) {
project := &storage.Project{
Name: createReq.Name,
Version: createReq.Version,
GitUrl: createReq.GitUrl,
}
if isValidProject(project) {
id, err := api.Database.SaveProject(project)
if err != nil {
r.Json(CreateProjectResponse{
Ok: false,
Message:err.Error(),
}, 500)
} else {
r.OkJson(CreateProjectResponse{
Ok: true,
Id: id,
})
}
} 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 {
if len(project.Name) <= 0 {
return false
}
return true
}
func (api *WebAPI) ProjectGet(r *Request) {
id, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64)
handleErr(err, r)
project := api.Database.GetProject(id)
if project != nil {
r.OkJson(GetProjectResponse{
Ok: true,
Project:project,
})
} else {
r.Json(GetProjectResponse{
Ok: false,
Message: "Project not found",
}, 404)
}
}

73
api/task.go Normal file
View File

@@ -0,0 +1,73 @@
package api
import (
"github.com/Sirupsen/logrus"
"src/task_tracker/storage"
)
type CreateTaskRequest struct {
Project int64 `json:"project"`
MaxRetries int64 `json:"max_retries"`
Recipe string `json:"recipe"`
}
type CreateTaskResponse struct {
Ok bool
Message string
}
func (api *WebAPI) TaskCreate(r *Request) {
var createReq CreateTaskRequest
if r.GetJson(&createReq) {
task := &storage.Task{
Project:createReq.Project,
MaxRetries: createReq.MaxRetries,
Recipe:createReq.Recipe,
}
if isTaskValid(task) {
err := api.Database.SaveTask(task)
if err != nil {
r.Json(CreateTaskResponse{
Ok: false,
Message: err.Error(), //todo: hide sensitive error?
}, 500)
} else {
r.OkJson(CreateTaskResponse{
Ok: true,
})
}
} else {
logrus.WithFields(logrus.Fields{
"task": task,
}).Warn("Invalid task")
r.Json(CreateTaskResponse{
Ok: false,
Message: "Invalid task",
}, 400)
}
}
}
func isTaskValid(task *storage.Task) bool {
if task.MaxRetries < 0 {
return false
}
if task.Project <= 0 {
return false
}
if len(task.Recipe) <= 0 {
return false
}
return true
}
func (api *WebAPI) TaskGet(r *Request) {
}

105
api/worker.go Normal file
View File

@@ -0,0 +1,105 @@
package api
import (
"github.com/Sirupsen/logrus"
"github.com/google/uuid"
"src/task_tracker/storage"
"time"
)
type CreateWorkerRequest struct {
}
type CreateWorkerResponse struct {
Ok bool `json:"ok"`
Message string `json:"message,omitempty"`
WorkerId string `json:"id,omitempty"`
}
type GetWorkerResponse struct {
Ok bool `json:"ok"`
Message string `json:"message,omitempty"`
Worker *storage.Worker `json:"worker,omitempty"`
}
func (api *WebAPI) WorkerCreate(r *Request) {
workerReq := &CreateWorkerRequest{}
if r.GetJson(workerReq) {
identity := getIdentity(r)
if canCreateWorker(r, workerReq, identity) {
id, err := api.workerCreate(workerReq, getIdentity(r))
if err != nil {
handleErr(err, r)
} else {
r.OkJson(CreateWorkerResponse{
Ok: true,
WorkerId: id.String(),
})
}
} else {
r.Json(CreateWorkerResponse{
Ok: false,
Message: "You are now allowed to create a worker",
}, 403)
}
}
}
func (api *WebAPI) WorkerGet(r *Request) {
id, err := uuid.Parse(r.Ctx.UserValue("id").(string))
if err != nil {
logrus.WithFields(logrus.Fields{
"id": id,
}).Warn("Invalid UUID")
r.Json(GetWorkerResponse{
Ok: false,
Message:err.Error(),
}, 400)
return
}
worker := api.Database.GetWorker(id)
if worker != nil {
r.OkJson(GetWorkerResponse{
Ok: true,
Worker:worker,
})
} else {
r.Json(GetWorkerResponse{
Ok: false,
Message:"Worker not found",
}, 404)
}
}
func (api *WebAPI) workerCreate(request *CreateWorkerRequest, identity *storage.Identity) (uuid.UUID, error) {
worker := storage.Worker{
Id: uuid.New(),
Created: time.Now().Unix(),
Identity: identity,
}
api.Database.SaveWorker(&worker)
return worker.Id, nil
}
func canCreateWorker(r *Request, cwr *CreateWorkerRequest, identity *storage.Identity) bool {
return true
}
func getIdentity(r *Request) *storage.Identity {
identity := storage.Identity{
RemoteAddr: r.Ctx.RemoteAddr().String(),
}
return &identity
}