package service import ( "context" "os" "gitlab.com/uafrica/go-utils/audit" "gitlab.com/uafrica/go-utils/errors" "gitlab.com/uafrica/go-utils/logger" "gitlab.com/uafrica/go-utils/string_utils" ) type Service interface { logger.Logger IErrorReporter Producer audit.Auditor WithStarter(name string, starter IStarter) Service WithProducer(producer Producer) Service WithAuditor(auditor audit.Auditor) Service WithErrorReporter(reporter IErrorReporter) Service NewContext(base context.Context, requestID string, values map[string]interface{}) (Context, error) } func New() Service { env := os.Getenv("ENVIRONMENT") //todo: support config loading for local dev and env for lambda in prod if env == "" { env = "dev" } return service{ Producer: nil, Logger: logger.New().WithFields(map[string]interface{}{"env": env}), IErrorReporter: DoNotReportErrors{}, Auditor: audit.None(), env: env, starters: map[string]IStarter{}, } } type service struct { logger.Logger //for logging outside of context Producer //for sending async events IErrorReporter audit.Auditor env string starters map[string]IStarter } func (s service) Env() string { return s.env } //adds a starter function to call in each new context //they will be called in the sequence they were added (before api/cron/queue checks) //and they do not have details about the event //if starter returns error, processing fails //if starter succeeds, and return !=nil data, it is stored against the name // so your handler can retieve it with: // checkData := ctx.Value(name).(expectedType) // or // checkData,ok := ctx.Value(name).(expectedType) // if !ok { ... } //you can implement one starter that does everything and return a struct or //implement one for your db, one for rate limit, one for ... //the name must be snake-case, e.g. "this_is_my_starter_name" func (s service) WithStarter(name string, starter IStarter) Service { if !string_utils.IsSnakeCase(name) { panic(errors.Errorf("invalid starter name=\"%s\", expecting snake_case names only", name)) } if starter == nil { panic(errors.Errorf("starter(%s)==nil", name)) } if _, ok := s.starters[name]; ok { panic(errors.Errorf("starter(%s) already defined", name)) } s.starters[name] = starter return s } func (s service) WithProducer(producer Producer) Service { if producer != nil { s.Producer = producer } return s } func (s service) WithErrorReporter(reporter IErrorReporter) Service { if reporter == nil { panic(errors.Errorf("ErrorReporter==nil")) } s.IErrorReporter = reporter return s } func (s service) WithAuditor(auditor audit.Auditor) Service { if auditor != nil { s.Auditor = auditor } return s } type IErrorReporter interface { ReportError(fields map[string]interface{}, err error) } type DoNotReportErrors struct{} func (DoNotReportErrors) ReportError(fields map[string]interface{}, err error) {}