10 changed files with 409 additions and 6 deletions
@ -0,0 +1,34 @@
|
||||
/* |
||||
------------------------------------------------------------------------------------------------------------------------ |
||||
####### dune ####### Copyright (c) 2021 losyme ##################################################### MIT License ####### |
||||
------------------------------------------------------------------------------------------------------------------------ |
||||
*/ |
||||
|
||||
package model |
||||
|
||||
import ( |
||||
"forge.chapril.org/dune/jw" |
||||
"forge.chapril.org/losyme/errors" |
||||
"forge.chapril.org/losyme/logger" |
||||
) |
||||
|
||||
type Config struct { |
||||
Logger *logger.Component |
||||
Storage jw.Storage |
||||
} |
||||
|
||||
func (cfg *Config) validate() error { |
||||
if cfg.Logger == nil { |
||||
return errors.New("[model] logger cannot be nil") //////////////////////////////////////////////////////////////
|
||||
} |
||||
|
||||
if cfg.Storage == nil { |
||||
return errors.New("[model] storage cannot be nil") /////////////////////////////////////////////////////////////
|
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
/* |
||||
######################################################################################################## @(°_°)@ ####### |
||||
*/ |
@ -0,0 +1,86 @@
|
||||
/* |
||||
------------------------------------------------------------------------------------------------------------------------ |
||||
####### dune ####### Copyright (c) 2021 losyme ##################################################### MIT License ####### |
||||
------------------------------------------------------------------------------------------------------------------------ |
||||
*/ |
||||
|
||||
package model |
||||
|
||||
import ( |
||||
"time" |
||||
|
||||
"forge.chapril.org/dune/jw" |
||||
"forge.chapril.org/losyme/errors" |
||||
) |
||||
|
||||
func (m *model) insertJob(job *jw.Job) (bool, error) { |
||||
done, err := m.storage.InsertJob(job) |
||||
if err != nil { |
||||
m.logger.Error( //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||
"Unable to insert a new job", |
||||
append( |
||||
job.Fields(), |
||||
"reason", err, |
||||
)..., |
||||
) |
||||
|
||||
return false, err |
||||
} |
||||
|
||||
if !done { |
||||
m.logger.Notice("A job with the same category already exists", job.Fields()...) //::::::::::::::::::::::::::::::
|
||||
return false, nil |
||||
} |
||||
|
||||
m.logger.Info("New job", job.Fields()...) //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||
|
||||
return true, nil |
||||
} |
||||
|
||||
func (m *model) CreateJob(job *jw.Job) (*jw.Job, error) { |
||||
if err := m.validateJob(job); err != nil { |
||||
return nil, errors.Wrap( ///////////////////////////////////////////////////////////////////////////////////////
|
||||
ErrValidation, |
||||
errors.WithMessage(err, "unable to create this job"), |
||||
) |
||||
} |
||||
|
||||
done, err := m.insertJob(job) |
||||
if err != nil { |
||||
return nil, errors.Wrap( ///////////////////////////////////////////////////////////////////////////////////////
|
||||
ErrInternal, |
||||
errors.WithMessage(err, "unable to insert this job"), |
||||
) |
||||
} |
||||
|
||||
if done { |
||||
return job, nil |
||||
} |
||||
|
||||
return nil, nil |
||||
} |
||||
|
||||
func (m *model) NextJob(namespace string) (*jw.Job, error) { |
||||
return nil, nil |
||||
} |
||||
|
||||
func (m *model) UpdateJob(job *jw.Job) (*jw.Job, error) { |
||||
return nil, nil |
||||
} |
||||
|
||||
func (m *model) NotifyJob(id string, data interface{}) error { |
||||
//@todo
|
||||
return nil |
||||
} |
||||
|
||||
func (m *model) SetJobPriority(id string, priority jw.Priority) error { |
||||
return m.storage.SetJobPriority(id, priority) |
||||
} |
||||
|
||||
func (m *model) SetJobRunAfter(id string, duration time.Duration) error { |
||||
return m.storage.SetJobRunAfter(id, duration) |
||||
} |
||||
|
||||
/* |
||||
######################################################################################################## @(°_°)@ ####### |
||||
*/ |
@ -0,0 +1,40 @@
|
||||
/* |
||||
------------------------------------------------------------------------------------------------------------------------ |
||||
####### dune ####### Copyright (c) 2021 losyme ##################################################### MIT License ####### |
||||
------------------------------------------------------------------------------------------------------------------------ |
||||
*/ |
||||
|
||||
package model |
||||
|
||||
import ( |
||||
"forge.chapril.org/dune/jw" |
||||
"forge.chapril.org/losyme/errors" |
||||
"forge.chapril.org/losyme/logger" |
||||
) |
||||
|
||||
const ( |
||||
ErrInternal = errors.Sentinel("internal error") |
||||
ErrValidation = errors.Sentinel("validation error") |
||||
) |
||||
|
||||
type model struct { |
||||
logger *logger.Component |
||||
storage jw.Storage |
||||
} |
||||
|
||||
func New(cfg *Config) (jw.Model, error) { |
||||
if err := cfg.validate(); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
m := &model{ |
||||
logger: cfg.Logger, |
||||
storage: cfg.Storage, |
||||
} |
||||
|
||||
return m, nil |
||||
} |
||||
|
||||
/* |
||||
######################################################################################################## @(°_°)@ ####### |
||||
*/ |
@ -0,0 +1,214 @@
|
||||
/* |
||||
------------------------------------------------------------------------------------------------------------------------ |
||||
####### dune ####### Copyright (c) 2021 losyme ##################################################### MIT License ####### |
||||
------------------------------------------------------------------------------------------------------------------------ |
||||
*/ |
||||
|
||||
package model |
||||
|
||||
import ( |
||||
"time" |
||||
|
||||
"forge.chapril.org/dune/jw" |
||||
"forge.chapril.org/losyme/errors" |
||||
"forge.chapril.org/losyme/uuid" |
||||
) |
||||
|
||||
func validateID(id *string) error { |
||||
if *id == "" { |
||||
*id = uuid.New() |
||||
} else if !uuid.IsValid(*id) { |
||||
return errors.New( /////////////////////////////////////////////////////////////////////////////////////////////
|
||||
"this identifier is not a UUID", |
||||
"id", *id, |
||||
) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func validatePriority(p *jw.Priority) { |
||||
if *p < jw.PriorityNone { |
||||
*p = jw.PriorityNone |
||||
} else if *p > jw.PriorityCritical { |
||||
*p = jw.PriorityCritical |
||||
} |
||||
} |
||||
|
||||
func (m *model) validateJob(job *jw.Job) error { |
||||
if err := m.storage.ValidateJob(job); err != nil { |
||||
return err |
||||
} |
||||
|
||||
if err := validateID(&job.ID); err != nil { |
||||
return err |
||||
} |
||||
|
||||
if job.Name == "" { |
||||
job.Name = "?" |
||||
} |
||||
|
||||
if job.Namespace == "" { |
||||
return errors.New("namespace must not be empty") ///////////////////////////////////////////////////////////////
|
||||
} |
||||
|
||||
if job.Type == "" { |
||||
return errors.New("type must not be empty") ////////////////////////////////////////////////////////////////////
|
||||
} |
||||
|
||||
if job.Origin == "" { |
||||
job.Origin = "?" |
||||
} |
||||
|
||||
validatePriority(&job.Priority) |
||||
|
||||
if job.Public == nil { |
||||
job.Public = make(map[string]interface{}) |
||||
} |
||||
|
||||
if job.Private == nil { |
||||
job.Private = make(map[string]interface{}) |
||||
} |
||||
|
||||
if job.MaxAttempts < 0 { |
||||
job.MaxAttempts = 0 |
||||
} |
||||
|
||||
job.Workflow = nil |
||||
job.CreatedAt = time.Now() |
||||
job.Status = jw.StatusTodo |
||||
job.Attempt = 0 |
||||
job.Session = 0 |
||||
job.Result = nil |
||||
job.FinishedAt = nil |
||||
job.Weight = 0 |
||||
job.TimeReference = job.CreatedAt |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func validateNext(wf *jw.Workflow, next map[string]interface{}) error { |
||||
if next == nil { |
||||
return nil |
||||
} |
||||
|
||||
for key, v := range next { |
||||
switch value := v.(type) { |
||||
case nil: |
||||
// ok
|
||||
case string: |
||||
if _, ok := wf.AllSteps[value]; !ok { |
||||
return errors.New( /////////////////////////////////////////////////////////////////////////////////////
|
||||
"the step associated with this key does not exist", |
||||
"key", key, |
||||
"step", value, |
||||
) |
||||
} |
||||
case map[string]interface{}: |
||||
for k, v := range value { |
||||
switch k { |
||||
case "config": |
||||
// ok
|
||||
case "step": |
||||
s, ok := v.(string) |
||||
if !ok { |
||||
return errors.New( /////////////////////////////////////////////////////////////////////////////
|
||||
"the 'step' key must be a string", |
||||
"key", key, |
||||
) |
||||
} |
||||
|
||||
if _, ok := wf.AllSteps[s]; !ok { |
||||
return errors.New( /////////////////////////////////////////////////////////////////////////////
|
||||
"the step associated with these keys does not exist", |
||||
"keys", key+"/"+k, |
||||
"step", s, |
||||
) |
||||
} |
||||
default: |
||||
return errors.New( /////////////////////////////////////////////////////////////////////////////////
|
||||
"the last of these keys is not known", |
||||
"keys", key+"/"+k, |
||||
) |
||||
} |
||||
} |
||||
default: |
||||
return errors.New( /////////////////////////////////////////////////////////////////////////////////////////
|
||||
"the value associated with this key is not valid", |
||||
"key", key, |
||||
) |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func validateStep(wf *jw.Workflow, step *jw.Step) error { |
||||
if step.Namespace == "" { |
||||
return errors.New("namespace must not be empty") ///////////////////////////////////////////////////////////////
|
||||
} |
||||
|
||||
if step.Type == "" { |
||||
return errors.New("type must not be empty") ////////////////////////////////////////////////////////////////////
|
||||
} |
||||
|
||||
return validateNext(wf, step.Next) |
||||
} |
||||
|
||||
func (m *model) validateWorkflow(wf *jw.Workflow) error { |
||||
if err := m.storage.ValidateWorkflow(wf); err != nil { |
||||
return err |
||||
} |
||||
|
||||
if err := validateID(&wf.ID); err != nil { |
||||
return err |
||||
} |
||||
|
||||
if wf.Type == "" { |
||||
wf.Type = "?" |
||||
} |
||||
|
||||
if wf.Description == "" { |
||||
wf.Description = "?" |
||||
} |
||||
|
||||
if wf.Origin == "" { |
||||
wf.Origin = "?" |
||||
} |
||||
|
||||
if wf.FirstStep == "" { |
||||
return errors.New("first step must not be empty") //////////////////////////////////////////////////////////////
|
||||
} |
||||
|
||||
if len(wf.AllSteps) == 0 { |
||||
return errors.New("there are no steps") ////////////////////////////////////////////////////////////////////////
|
||||
} |
||||
|
||||
if _, ok := wf.AllSteps[wf.FirstStep]; !ok { |
||||
return errors.New("first step does not match any step") ////////////////////////////////////////////////////////
|
||||
} |
||||
|
||||
for name, step := range wf.AllSteps { |
||||
if err := validateStep(wf, step); err != nil { |
||||
return errors.WithMessage( /////////////////////////////////////////////////////////////////////////////////
|
||||
err, |
||||
"this step is not valid", |
||||
"name", name, |
||||
) |
||||
} |
||||
} |
||||
|
||||
if wf.Data == nil { |
||||
wf.Data = make(map[string]interface{}) |
||||
} |
||||
|
||||
wf.CreatedAt = time.Now() |
||||
wf.Status = jw.StatusRunning |
||||
wf.FinishedAt = nil |
||||
|
||||
return nil |
||||
} |
||||
|
||||
/* |
||||
######################################################################################################## @(°_°)@ ####### |
||||
*/ |
@ -0,0 +1,31 @@
|
||||
/* |
||||
------------------------------------------------------------------------------------------------------------------------ |
||||
####### dune ####### Copyright (c) 2021 losyme ##################################################### MIT License ####### |
||||
------------------------------------------------------------------------------------------------------------------------ |
||||
*/ |
||||
|
||||
package model |
||||
|
||||
import ( |
||||
"forge.chapril.org/dune/jw" |
||||
"forge.chapril.org/losyme/errors" |
||||
) |
||||
|
||||
func (m *model) CreateWorkflow(wf *jw.Workflow) (*jw.Workflow, error) { |
||||
if err := m.validateWorkflow(wf); err != nil { |
||||
return nil, errors.Wrap( ///////////////////////////////////////////////////////////////////////////////////////
|
||||
ErrValidation, |
||||
errors.WithMessage(err, "impossible to create this workflow"), |
||||
) |
||||
} |
||||
|
||||
return nil, nil |
||||
} |
||||
|
||||
func (m *model) SetWorkflowPriority(id string, priority jw.Priority) error { |
||||
return m.storage.SetWorkflowPriority(id, priority) |
||||
} |
||||
|
||||
/* |
||||
######################################################################################################## @(°_°)@ ####### |
||||
*/ |
Loading…
Reference in new issue