Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions pkg/debouncer/action/action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package action

import (
"context"
"errors"

"github.com/owncloud/reva/v2/pkg/debouncer/tasklist"
)

var (
ErrEmptyList = errors.New("Task list is empty")
)

// Action represents the action that the debouncer needs to perform
// over the provided task list.
// This action usually involves picking the "right" task, run it and
// return the error if any.
// Choosing the "right" task can be as easy as picking the first or last
// task of the list and run it, or more complex such as gathering info from
// all the tasks and create a new task based on the aggregated info.
// The action isn't limited to choosing just one task, and it can choose
// and run multiple tasks if needed (although not recommended).
type Action interface {
// RunTasks perform an action over the provided task list. This usually
// involves picking one task of the list and run it.
// It's expected that RunTasks will be executed in its own goroutine,
// using a new context.
RunTasks(ctx context.Context, tasks []tasklist.InternalTask) error
// GetId returns the ID of this instance. It must be unique, so multiple
// instances from the same action type must return different IDs.
// The recommendation is to use the instance type followed by a random
// number, such as "ChooseLast_123987"
GetId() string
// GetTracingData returns additional data that will be used for tracing.
// Consider this data as public information. This data is intended to
// be use purely for informational purposes. You can return nil if
// there isn't any data to be published.
GetTracingData() map[string]string
}
13 changes: 13 additions & 0 deletions pkg/debouncer/action/action_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package action_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestAction(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Debouncer action suite")
}
44 changes: 44 additions & 0 deletions pkg/debouncer/action/chooselast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package action

import (
"context"
"math/rand"
"strconv"

"github.com/owncloud/reva/v2/pkg/debouncer/tasklist"
)

// ChooseLast implements a debouncer action that will always run the last
// task of the list, which is expected to be the most recent one.
type ChooseLast struct {
id string
}

// NewChooseLast will return a new instance
func NewChooseLast() *ChooseLast {
return &ChooseLast{
id: "ChooseLast_" + strconv.FormatUint(rand.Uint64(), 10),
}
}

// RunTasks will run the last task of the provided list. If the list is empty
// a ErrEmptyList will be returned.
func (cl *ChooseLast) RunTasks(ctx context.Context, tasks []tasklist.InternalTask) error {
if len(tasks) <= 0 {
return ErrEmptyList
}

chosenTask := tasks[len(tasks)-1]
return chosenTask.OriginalTask.Execute(ctx)
}

// GetId will return the id of this instance. It will return "ChooseLast_"
// followed by a random number.
func (cl *ChooseLast) GetId() string {
return cl.id
}

// GetTracingData will return nil
func (cl *ChooseLast) GetTracingData() map[string]string {
return nil
}
80 changes: 80 additions & 0 deletions pkg/debouncer/action/chooselast_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package action_test

import (
"context"
"errors"

"github.com/owncloud/reva/v2/pkg/debouncer/action"
"github.com/owncloud/reva/v2/pkg/debouncer/tasklist"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

type DummyTask struct {
Err error
Done bool
}

func (d *DummyTask) ExposeData() map[string]string {
return nil
}

func (d *DummyTask) Execute(ctx context.Context) error {
d.Done = true
return d.Err
}

var _ = Describe("ChooseLast", func() {
var cl action.Action

BeforeEach(func() {
cl = action.NewChooseLast()
})

Describe("RunTasks", func() {
It("Choose last task", func() {
ctx := context.Background()
task1 := tasklist.NewInternalTaskFromTask(ctx, &DummyTask{})
task2 := tasklist.NewInternalTaskFromTask(ctx, &DummyTask{})
task3 := tasklist.NewInternalTaskFromTask(ctx, &DummyTask{})
task4 := tasklist.NewInternalTaskFromTask(ctx, &DummyTask{})
taskList := []tasklist.InternalTask{task1, task2, task3, task4}

err := cl.RunTasks(ctx, taskList)
Expect(err).To(Succeed())
Expect(task1.OriginalTask.(*DummyTask).Done).To(Equal(false))
Expect(task2.OriginalTask.(*DummyTask).Done).To(Equal(false))
Expect(task3.OriginalTask.(*DummyTask).Done).To(Equal(false))
Expect(task4.OriginalTask.(*DummyTask).Done).To(Equal(true))
})

It("Last task fails", func() {
ctx := context.Background()
terr := errors.New("oopsie!!")
task1 := tasklist.NewInternalTaskFromTask(ctx, &DummyTask{})
task2 := tasklist.NewInternalTaskFromTask(ctx, &DummyTask{})
task3 := tasklist.NewInternalTaskFromTask(ctx, &DummyTask{})
task4 := tasklist.NewInternalTaskFromTask(ctx, &DummyTask{Err: terr})
taskList := []tasklist.InternalTask{task1, task2, task3, task4}

err := cl.RunTasks(ctx, taskList)
Expect(err).To(Equal(terr))
Expect(task1.OriginalTask.(*DummyTask).Done).To(Equal(false))
Expect(task2.OriginalTask.(*DummyTask).Done).To(Equal(false))
Expect(task3.OriginalTask.(*DummyTask).Done).To(Equal(false))
Expect(task4.OriginalTask.(*DummyTask).Done).To(Equal(true))
})

It("Empty task list fails", func() {
taskList := []tasklist.InternalTask{}
err := cl.RunTasks(context.Background(), taskList)
Expect(err).To(Equal(action.ErrEmptyList))
})

It("Nil task list fails", func() {
err := cl.RunTasks(context.Background(), nil)
Expect(err).To(Equal(action.ErrEmptyList))
})
})
})
Loading