diff --git a/api/crm/spec.json b/api/crm/spec.json index c33df4fe2..e285a9d44 100644 --- a/api/crm/spec.json +++ b/api/crm/spec.json @@ -91,7 +91,7 @@ "name": "get", "method": "GET", "path": "/{workflowID}", - "title": "Get existing workflow details", + "title": "Get workflow details", "parameters": { "path": [ { @@ -163,6 +163,188 @@ } ] }, + { + "title": "Jobs", + "description": "Workflow Jobs", + "package": "crm", + "entrypoint": "job", + "path": "/job", + "authentication": [], + "struct": [ + { + "imports": [ + "github.com/crusttech/crust/crm/types", + "sqlxTypes github.com/jmoiron/sqlx/types" + ] + } + ], + "apis": [ + { + "name": "list", + "method": "GET", + "path": "/", + "title": "List jobs", + "parameters": { + "get": [ + { + "type": "string", + "name": "status", + "required": false, + "title": "Job status (`ok`, `error`, `running`, `cancelled` or `queued`)" + }, + { + "name": "page", + "type": "int", + "required": false, + "title": "Page number (0 based)" + }, + { + "name": "perPage", + "type": "int", + "required": false, + "title": "Returned items per page (default 50)" + } + ] + } + }, + { + "name": "run", + "path": "/", + "method": "POST", + "title": "Create a new job", + "parameters": { + "post": [ + { + "type": "string", + "name": "workflowID", + "required": true, + "title": "Workflow ID" + }, + { + "type": "string", + "name": "startAt", + "required": false, + "title": "Start datetime for a delayed job" + }, + { + "type": "types.JobParameterSet", + "name": "parameters", + "required": false, + "title": "Extra job parameters (map[string]string)" + } + ] + } + }, + { + "name": "get", + "method": "GET", + "path": "/{jobID}", + "title": "Get job details", + "parameters": { + "path": [ + { + "type": "string", + "name": "jobID", + "required": true, + "title": "Job ID" + } + ] + } + }, + { + "name": "logs", + "method": "GET", + "path": "/{jobID}/logs", + "title": "Get job logs", + "parameters": { + "path": [ + { + "type": "string", + "name": "jobID", + "required": true, + "title": "Job ID" + }, + { + "name": "page", + "type": "int", + "required": false, + "title": "Page number (0 based)" + }, + { + "name": "perPage", + "type": "int", + "required": false, + "title": "Returned items per page (default 50)" + } + ] + } + }, + { + "name": "update", + "method": "POST", + "path": "/{jobID}", + "title": "Update job details", + "parameters": { + "path": [ + { + "type": "string", + "name": "jobID", + "required": true, + "title": "Job ID" + } + ], + "post": [ + { + "type": "string", + "name": "status", + "required": false, + "title": "Job status (`ok`, `error`, `running`, `cancelled` or `queued`)" + }, + { + "type": "sqlxTypes.JSONText", + "name": "log", + "required": false, + "title": "Job log item (append-only)" + }, + { + "type": "string", + "name": "workflowID", + "required": false, + "title": "Workflow ID" + }, + { + "type": "string", + "name": "startAt", + "required": false, + "title": "Start datetime for a delayed job" + }, + { + "type": "types.JobParameterSet", + "name": "parameters", + "required": false, + "title": "Extra job parameters (map[string]string)" + } + ] + } + }, + { + "name": "delete", + "method": "DELETE", + "path": "/{jobID}", + "title": "Cancel job", + "parameters": { + "path": [ + { + "type": "string", + "name": "jobID", + "required": true, + "title": "Job ID" + } + ] + } + } + ] + }, { "title": "Pages", "description": "CRM module pages", diff --git a/api/crm/spec/job.json b/api/crm/spec/job.json new file mode 100644 index 000000000..18f7acb71 --- /dev/null +++ b/api/crm/spec/job.json @@ -0,0 +1,184 @@ +{ + "Title": "Jobs", + "Description": "Workflow Jobs", + "Package": "crm", + "Interface": "Job", + "Struct": [ + { + "imports": [ + "github.com/crusttech/crust/crm/types", + "sqlxTypes github.com/jmoiron/sqlx/types" + ] + } + ], + "Parameters": null, + "Protocol": "", + "Authentication": [], + "Path": "/job", + "APIs": [ + { + "Name": "list", + "Method": "GET", + "Title": "List jobs", + "Path": "/", + "Parameters": { + "get": [ + { + "name": "status", + "required": false, + "title": "Job status (`ok`, `error`, `running`, `cancelled` or `queued`)", + "type": "string" + }, + { + "name": "page", + "required": false, + "title": "Page number (0 based)", + "type": "int" + }, + { + "name": "perPage", + "required": false, + "title": "Returned items per page (default 50)", + "type": "int" + } + ] + } + }, + { + "Name": "run", + "Method": "POST", + "Title": "Create a new job", + "Path": "/", + "Parameters": { + "post": [ + { + "name": "workflowID", + "required": true, + "title": "Workflow ID", + "type": "string" + }, + { + "name": "startAt", + "required": false, + "title": "Start datetime for a delayed job", + "type": "string" + }, + { + "name": "parameters", + "required": false, + "title": "Extra job parameters (map[string]string)", + "type": "types.JobParameterSet" + } + ] + } + }, + { + "Name": "get", + "Method": "GET", + "Title": "Get job details", + "Path": "/{jobID}", + "Parameters": { + "path": [ + { + "name": "jobID", + "required": true, + "title": "Job ID", + "type": "string" + } + ] + } + }, + { + "Name": "logs", + "Method": "GET", + "Title": "Get job logs", + "Path": "/{jobID}/logs", + "Parameters": { + "path": [ + { + "name": "jobID", + "required": true, + "title": "Job ID", + "type": "string" + }, + { + "name": "page", + "required": false, + "title": "Page number (0 based)", + "type": "int" + }, + { + "name": "perPage", + "required": false, + "title": "Returned items per page (default 50)", + "type": "int" + } + ] + } + }, + { + "Name": "update", + "Method": "POST", + "Title": "Update job details", + "Path": "/{jobID}", + "Parameters": { + "path": [ + { + "name": "jobID", + "required": true, + "title": "Job ID", + "type": "string" + } + ], + "post": [ + { + "name": "status", + "required": false, + "title": "Job status (`ok`, `error`, `running`, `cancelled` or `queued`)", + "type": "string" + }, + { + "name": "log", + "required": false, + "title": "Job log item (append-only)", + "type": "sqlxTypes.JSONText" + }, + { + "name": "workflowID", + "required": false, + "title": "Workflow ID", + "type": "string" + }, + { + "name": "startAt", + "required": false, + "title": "Start datetime for a delayed job", + "type": "string" + }, + { + "name": "parameters", + "required": false, + "title": "Extra job parameters (map[string]string)", + "type": "types.JobParameterSet" + } + ] + } + }, + { + "Name": "delete", + "Method": "DELETE", + "Title": "Cancel job", + "Path": "/{jobID}", + "Parameters": { + "path": [ + { + "name": "jobID", + "required": true, + "title": "Job ID", + "type": "string" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/api/crm/spec/workflow.json b/api/crm/spec/workflow.json index a6f393cac..2a7342cba 100644 --- a/api/crm/spec/workflow.json +++ b/api/crm/spec/workflow.json @@ -59,7 +59,7 @@ { "Name": "get", "Method": "GET", - "Title": "Get existing workflow details", + "Title": "Get workflow details", "Path": "/{workflowID}", "Parameters": { "path": [ diff --git a/crm/repository/job.go b/crm/repository/job.go new file mode 100644 index 000000000..c72a2533d --- /dev/null +++ b/crm/repository/job.go @@ -0,0 +1,29 @@ +package repository + +import ( + "context" + + "github.com/titpetric/factory" + + _ "github.com/crusttech/crust/crm/types" +) + +type ( + JobRepository interface { + With(ctx context.Context, db *factory.DB) JobRepository + } + + job struct { + *repository + } +) + +func Job(ctx context.Context, db *factory.DB) JobRepository { + return (&job{}).With(ctx, db) +} + +func (r *job) With(ctx context.Context, db *factory.DB) JobRepository { + return &job{ + repository: r.repository.With(ctx, db), + } +} diff --git a/crm/rest/handlers/job.go b/crm/rest/handlers/job.go new file mode 100644 index 000000000..5ed5b916e --- /dev/null +++ b/crm/rest/handlers/job.go @@ -0,0 +1,107 @@ +package handlers + +/* + Hello! This file is auto-generated from `docs/src/spec.json`. + + For development: + In order to update the generated files, edit this file under the location, + add your struct fields, imports, API definitions and whatever you want, and: + + 1. run [spec](https://github.com/titpetric/spec) in the same folder, + 2. run `./_gen.php` in this folder. + + You may edit `job.go`, `job.util.go` or `job_test.go` to + implement your API calls, helper functions and tests. The file `job.go` + is only generated the first time, and will not be overwritten if it exists. +*/ + +import ( + "context" + "github.com/go-chi/chi" + "net/http" + + "github.com/titpetric/factory/resputil" + + "github.com/crusttech/crust/crm/rest/request" +) + +// Internal API interface +type JobAPI interface { + List(context.Context, *request.JobList) (interface{}, error) + Run(context.Context, *request.JobRun) (interface{}, error) + Get(context.Context, *request.JobGet) (interface{}, error) + Logs(context.Context, *request.JobLogs) (interface{}, error) + Update(context.Context, *request.JobUpdate) (interface{}, error) + Delete(context.Context, *request.JobDelete) (interface{}, error) +} + +// HTTP API interface +type Job struct { + List func(http.ResponseWriter, *http.Request) + Run func(http.ResponseWriter, *http.Request) + Get func(http.ResponseWriter, *http.Request) + Logs func(http.ResponseWriter, *http.Request) + Update func(http.ResponseWriter, *http.Request) + Delete func(http.ResponseWriter, *http.Request) +} + +func NewJob(jh JobAPI) *Job { + return &Job{ + List: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewJobList() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return jh.List(r.Context(), params) + }) + }, + Run: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewJobRun() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return jh.Run(r.Context(), params) + }) + }, + Get: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewJobGet() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return jh.Get(r.Context(), params) + }) + }, + Logs: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewJobLogs() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return jh.Logs(r.Context(), params) + }) + }, + Update: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewJobUpdate() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return jh.Update(r.Context(), params) + }) + }, + Delete: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewJobDelete() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return jh.Delete(r.Context(), params) + }) + }, + } +} + +func (jh *Job) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) { + r.Group(func(r chi.Router) { + r.Use(middlewares...) + r.Route("/job", func(r chi.Router) { + r.Get("/", jh.List) + r.Post("/", jh.Run) + r.Get("/{jobID}", jh.Get) + r.Get("/{jobID}/logs", jh.Logs) + r.Post("/{jobID}", jh.Update) + r.Delete("/{jobID}", jh.Delete) + }) + }) +} diff --git a/crm/rest/job.go b/crm/rest/job.go new file mode 100644 index 000000000..81520951f --- /dev/null +++ b/crm/rest/job.go @@ -0,0 +1,46 @@ +package rest + +import ( + "context" + + "github.com/pkg/errors" + + "github.com/crusttech/crust/crm/rest/request" + "github.com/crusttech/crust/crm/service" +) + +var _ = errors.Wrap + +type ( + Job struct { + job service.JobService + } +) + +func (Job) New(jobSvc service.JobService) *Job { + return &Job{jobSvc} +} + +func (ctrl *Job) List(ctx context.Context, r *request.JobList) (interface{}, error) { + return nil, errors.New("Not implemented: Job.list") +} + +func (ctrl *Job) Run(ctx context.Context, r *request.JobRun) (interface{}, error) { + return nil, errors.New("Not implemented: Job.run") +} + +func (ctrl *Job) Get(ctx context.Context, r *request.JobGet) (interface{}, error) { + return nil, errors.New("Not implemented: Job.get") +} + +func (ctrl *Job) Logs(ctx context.Context, r *request.JobLogs) (interface{}, error) { + return nil, errors.New("Not implemented: Job.logs") +} + +func (ctrl *Job) Update(ctx context.Context, r *request.JobUpdate) (interface{}, error) { + return nil, errors.New("Not implemented: Job.update") +} + +func (ctrl *Job) Delete(ctx context.Context, r *request.JobDelete) (interface{}, error) { + return nil, errors.New("Not implemented: Job.delete") +} diff --git a/crm/rest/request/job.go b/crm/rest/request/job.go new file mode 100644 index 000000000..f8269f736 --- /dev/null +++ b/crm/rest/request/job.go @@ -0,0 +1,340 @@ +package request + +/* + Hello! This file is auto-generated from `docs/src/spec.json`. + + For development: + In order to update the generated files, edit this file under the location, + add your struct fields, imports, API definitions and whatever you want, and: + + 1. run [spec](https://github.com/titpetric/spec) in the same folder, + 2. run `./_gen.php` in this folder. + + You may edit `job.go`, `job.util.go` or `job_test.go` to + implement your API calls, helper functions and tests. The file `job.go` + is only generated the first time, and will not be overwritten if it exists. +*/ + +import ( + "encoding/json" + "io" + "mime/multipart" + "net/http" + "strings" + + "github.com/go-chi/chi" + "github.com/pkg/errors" + + "github.com/crusttech/crust/crm/types" + sqlxTypes "github.com/jmoiron/sqlx/types" +) + +var _ = chi.URLParam +var _ = multipart.FileHeader{} + +// Job list request parameters +type JobList struct { + Status string + Page int + PerPage int +} + +func NewJobList() *JobList { + return &JobList{} +} + +func (j *JobList) Fill(r *http.Request) (err error) { + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(j) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + if err = r.ParseForm(); err != nil { + return err + } + + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + if val, ok := get["status"]; ok { + + j.Status = val + } + if val, ok := get["page"]; ok { + + j.Page = parseInt(val) + } + if val, ok := get["perPage"]; ok { + + j.PerPage = parseInt(val) + } + + return err +} + +var _ RequestFiller = NewJobList() + +// Job run request parameters +type JobRun struct { + WorkflowID string + StartAt string + Parameters types.JobParameterSet +} + +func NewJobRun() *JobRun { + return &JobRun{} +} + +func (j *JobRun) Fill(r *http.Request) (err error) { + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(j) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + if err = r.ParseForm(); err != nil { + return err + } + + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + if val, ok := post["workflowID"]; ok { + + j.WorkflowID = val + } + if val, ok := post["startAt"]; ok { + + j.StartAt = val + } + + return err +} + +var _ RequestFiller = NewJobRun() + +// Job get request parameters +type JobGet struct { + JobID string +} + +func NewJobGet() *JobGet { + return &JobGet{} +} + +func (j *JobGet) Fill(r *http.Request) (err error) { + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(j) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + if err = r.ParseForm(); err != nil { + return err + } + + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + j.JobID = chi.URLParam(r, "jobID") + + return err +} + +var _ RequestFiller = NewJobGet() + +// Job logs request parameters +type JobLogs struct { + JobID string + Page int + PerPage int +} + +func NewJobLogs() *JobLogs { + return &JobLogs{} +} + +func (j *JobLogs) Fill(r *http.Request) (err error) { + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(j) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + if err = r.ParseForm(); err != nil { + return err + } + + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + j.JobID = chi.URLParam(r, "jobID") + j.Page = parseInt(chi.URLParam(r, "page")) + j.PerPage = parseInt(chi.URLParam(r, "perPage")) + + return err +} + +var _ RequestFiller = NewJobLogs() + +// Job update request parameters +type JobUpdate struct { + JobID string + Status string + Log sqlxTypes.JSONText + WorkflowID string + StartAt string + Parameters types.JobParameterSet +} + +func NewJobUpdate() *JobUpdate { + return &JobUpdate{} +} + +func (j *JobUpdate) Fill(r *http.Request) (err error) { + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(j) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + if err = r.ParseForm(); err != nil { + return err + } + + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + j.JobID = chi.URLParam(r, "jobID") + if val, ok := post["status"]; ok { + + j.Status = val + } + if val, ok := post["log"]; ok { + + if j.Log, err = parseJSONTextWithErr(val); err != nil { + return err + } + } + if val, ok := post["workflowID"]; ok { + + j.WorkflowID = val + } + if val, ok := post["startAt"]; ok { + + j.StartAt = val + } + + return err +} + +var _ RequestFiller = NewJobUpdate() + +// Job delete request parameters +type JobDelete struct { + JobID string +} + +func NewJobDelete() *JobDelete { + return &JobDelete{} +} + +func (j *JobDelete) Fill(r *http.Request) (err error) { + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(j) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + if err = r.ParseForm(); err != nil { + return err + } + + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + j.JobID = chi.URLParam(r, "jobID") + + return err +} + +var _ RequestFiller = NewJobDelete() diff --git a/crm/rest/router.go b/crm/rest/router.go index e03d9844c..dc8b8aa47 100644 --- a/crm/rest/router.go +++ b/crm/rest/router.go @@ -15,6 +15,7 @@ func MountRoutes(jwtAuth auth.TokenEncoder) func(chi.Router) { contentSvc = service.Content() pageSvc = service.Page() workflowSvc = service.Workflow() + jobSvc = service.Job() ) var ( @@ -22,6 +23,7 @@ func MountRoutes(jwtAuth auth.TokenEncoder) func(chi.Router) { module = Module{}.New(moduleSvc, contentSvc) page = Page{}.New(pageSvc) workflow = Workflow{}.New(workflowSvc) + job = Job{}.New(jobSvc) ) // Initialize handers & controllers. @@ -34,6 +36,7 @@ func MountRoutes(jwtAuth auth.TokenEncoder) func(chi.Router) { handlers.NewPage(page).MountRoutes(r) handlers.NewModule(module).MountRoutes(r) handlers.NewWorkflow(workflow).MountRoutes(r) + handlers.NewJob(job).MountRoutes(r) }) } } diff --git a/crm/rest/workflow.go b/crm/rest/workflow.go index 89815af9a..b19e3536c 100644 --- a/crm/rest/workflow.go +++ b/crm/rest/workflow.go @@ -2,9 +2,11 @@ package rest import ( "context" + + "github.com/pkg/errors" + "github.com/crusttech/crust/crm/rest/request" "github.com/crusttech/crust/crm/service" - "github.com/pkg/errors" ) var _ = errors.Wrap diff --git a/crm/service/job.go b/crm/service/job.go new file mode 100644 index 000000000..29b3b2cb3 --- /dev/null +++ b/crm/service/job.go @@ -0,0 +1,36 @@ +package service + +import ( + "context" + + "github.com/titpetric/factory" + + "github.com/crusttech/crust/crm/repository" + _ "github.com/crusttech/crust/crm/types" +) + +type ( + job struct { + db *factory.DB + ctx context.Context + + repository repository.JobRepository + } + + JobService interface { + With(ctx context.Context) JobService + } +) + +func Job() JobService { + return (&job{}).With(context.Background()) +} + +func (s *job) With(ctx context.Context) JobService { + db := repository.DB(ctx) + return &job{ + db: db, + ctx: ctx, + repository: repository.Job(ctx, db), + } +} diff --git a/crm/service/service.go b/crm/service/service.go index 015461ebc..08cbadb26 100644 --- a/crm/service/service.go +++ b/crm/service/service.go @@ -11,6 +11,7 @@ var ( DefaultModule ModuleService DefaultPage PageService DefaultWorkflow WorkflowService + DefaultJob JobService ) func Init() { @@ -20,5 +21,6 @@ func Init() { DefaultModule = Module() DefaultPage = Page() DefaultWorkflow = Workflow() + DefaultJob = Job() }) } diff --git a/crm/types/job.go b/crm/types/job.go new file mode 100644 index 000000000..f1ee7a159 --- /dev/null +++ b/crm/types/job.go @@ -0,0 +1,5 @@ +package types + +type ( + JobParameterSet map[string]string +) diff --git a/docs/crm/README.md b/docs/crm/README.md index 0935274ae..37fd6b676 100644 --- a/docs/crm/README.md +++ b/docs/crm/README.md @@ -32,6 +32,108 @@ CRM input field definitions +# Jobs + +Workflow Jobs + +## List jobs + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/job/` | HTTP/S | GET | | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | +| status | string | GET | Job status (`ok`, `error`, `running`, `cancelled` or `queued`) | N/A | NO | +| page | int | GET | Page number (0 based) | N/A | NO | +| perPage | int | GET | Returned items per page (default 50) | N/A | NO | + +## Create a new job + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/job/` | HTTP/S | POST | | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | +| workflowID | string | POST | Workflow ID | N/A | YES | +| startAt | string | POST | Start datetime for a delayed job | N/A | NO | +| parameters | types.JobParameterSet | POST | Extra job parameters (map[string]string) | N/A | NO | + +## Get job details + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/job/{jobID}` | HTTP/S | GET | | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | +| jobID | string | PATH | Job ID | N/A | YES | + +## Get job logs + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/job/{jobID}/logs` | HTTP/S | GET | | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | +| jobID | string | PATH | Job ID | N/A | YES | +| page | int | PATH | Page number (0 based) | N/A | NO | +| perPage | int | PATH | Returned items per page (default 50) | N/A | NO | + +## Update job details + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/job/{jobID}` | HTTP/S | POST | | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | +| jobID | string | PATH | Job ID | N/A | YES | +| status | string | POST | Job status (`ok`, `error`, `running`, `cancelled` or `queued`) | N/A | NO | +| log | sqlxTypes.JSONText | POST | Job log item (append-only) | N/A | NO | +| workflowID | string | POST | Workflow ID | N/A | NO | +| startAt | string | POST | Start datetime for a delayed job | N/A | NO | +| parameters | types.JobParameterSet | POST | Extra job parameters (map[string]string) | N/A | NO | + +## Cancel job + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/job/{jobID}` | HTTP/S | DELETE | | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | +| jobID | string | PATH | Job ID | N/A | YES | + + + + # Modules CRM module definitions @@ -550,7 +652,7 @@ CRM workflow definitions | onError | types.WorkflowTaskSet | POST | Type ID | N/A | NO | | timeout | int | POST | Timeout in seconds | N/A | NO | -## Get existing workflow details +## Get workflow details #### Method