func (o *Operation) Render() Op {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return Op{
		ID:         o.id,
		Class:      o.class.String(),
		Status:     o.status.String(),
		StatusCode: o.status,
		URL:        fmt.Sprintf("/operations/%s", o.id),
		Err:        o.err,
	}
}
Render the operation as a readonly Op
package operations
import (
	"fmt"
	"sync"
	"time"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/pborman/uuid"
	"github.com/pkg/errors"
)
type class int
const (
	Task class = iota
)
func (c class) String() string {
	switch c {
	case Task:
		return "task"
	default:
		return "unknown"
	}
}
type Op struct {
	ID         string
	Class      string
	Status     string
	StatusCode Status
	URL        string
	Err        string
}
type Changer interface {
	Change(Op)
}
type Operation struct {
	id       string
	class    class
	status   Status
	err      string
	readOnly bool
	chanDone chan error
	mutex    sync.RWMutex
	hookRun    func(*Operation) error
	hookCancel func(*Operation) error
	logger  log.Logger
	changer Changer
}
func NewOperation(hook func(*Operation) error, changer Changer, options ...Option) *Operation {
	opts := newOptions()
	for _, option := range options {
		option(opts)
	}
	return &Operation{
		id:       uuid.NewRandom().String(),
		class:    Task,
		status:   Pending,
		chanDone: make(chan error),
		hookRun:  hook,
		changer:  changer,
		logger:   opts.logger,
	}
}
func (o *Operation) Run() (<-chan error, error) {
	if o.status != Pending {
		return nil, errors.Errorf("only pending operations can be started")
	}
	chanRun := make(chan error, 1)
	o.setStatus(Running)
	if o.hookRun != nil {
		go o.runHook(chanRun, o.hookRun)
	} else {
		o.setStatus(Success)
		o.done()
		chanRun <- nil
	}
	return chanRun, nil
}
func (o *Operation) Cancel() (<-chan error, error) {
	if o.status != Running {
		return nil, errors.Errorf("only running operations can be cancelled")
	}
	chanCancel := make(chan error, 1)
	o.setStatus(Cancelling)
	if o.hookCancel != nil {
		go o.cancelHook(chanCancel, o.hookCancel)
	} else {
		o.setStatus(Cancelled)
		o.done()
		chanCancel <- nil
	}
	return chanCancel, nil
}
func (o *Operation) Wait(timeout time.Duration) (bool, error) {
	if o.status.IsFinal() {
		return true, nil
	}
	if timeout == -1 {
		select {
		case <-o.chanDone:
			return true, nil
		}
	}
	if timeout > 0 {
		timer := time.NewTimer(timeout)
		select {
		case <-o.chanDone:
			return true, nil
		case <-timer.C:
			return false, errors.Errorf("timeout")
		}
	}
	return false, nil
}
func (o *Operation) IsFinal() bool {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.status.IsFinal()
}