Skip to content

Commit

Permalink
Revert "Change most fields to private, and add constructor functions"
Browse files Browse the repository at this point in the history
This reverts commit 2db4e6a.
  • Loading branch information
mike-gregory-ovo committed Jun 22, 2023
1 parent 76ef014 commit c245f90
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 112 deletions.
102 changes: 39 additions & 63 deletions sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ const (
)

type Sync struct {
DryRun bool // DryRun mode calculates membership, but doesn't add or remove.

operatingMode OperatingMode // Change the order of Sync's operation. Default is RemoveAdd.
caseSensitive bool // caseSensitive sets if Go Sync is case-sensitive. Default is true.
DryRun bool // DryRun mode calculates membership, but doesn't add or remove.
OperatingMode OperatingMode // Change the order of Sync's operation. Default is RemoveAdd.
CaseSensitive bool // CaseSensitive sets if Go Sync is case-sensitive. Default is true.
source Adapter // The source adapter.
cache map[string]bool // cache prevents polling the source more than once.
/*
maximumChanges sets the maximum number of allowed changes per add/remove operation. It is not a cumulative
MaximumChanges sets the maximum number of allowed changes per add/remove operation. It is not a cumulative
total, and the number only applies to each distinct operation.
For example:
Expand All @@ -43,18 +44,35 @@ type Sync struct {
Default is NoChangeLimit (or -1).
*/
maximumChanges int
source Adapter // The source adapter.
cache map[string]bool // cache prevents polling the source more than once.
logger *log.Logger
MaximumChanges int
Logger *log.Logger
}

// New creates a new Sync service.
func New(source Adapter, optsFn ...func(*Sync)) *Sync {
sync := &Sync{
DryRun: false,
OperatingMode: RemoveAdd,
CaseSensitive: true,
source: source,
cache: make(map[string]bool),
MaximumChanges: NoChangeLimit,
Logger: log.New(os.Stderr, "[go-sync/sync] ", log.LstdFlags|log.Lshortfile|log.Lmsgprefix),
}

for _, fn := range optsFn {
fn(sync)
}

return sync
}

// generateHashMap takes a list of strings and returns a hashed map of { item => true }.
func (s *Sync) generateHashMap(i []string) map[string]bool {
out := map[string]bool{}

for _, str := range i {
if s.caseSensitive {
if s.CaseSensitive {
out[str] = true
} else {
out[strings.ToLower(str)] = true
Expand Down Expand Up @@ -95,7 +113,7 @@ func (s *Sync) getThingsToRemove(things []string) []string {
// generateCache populates the cache with a map of things for efficient lookup.
func (s *Sync) generateCache(ctx context.Context) error {
if len(s.cache) == 0 {
s.logger.Println("Getting things from source adapter")
s.Logger.Println("Getting things from source adapter")

things, err := s.source.Get(ctx)
if err != nil {
Expand All @@ -117,17 +135,17 @@ func (s *Sync) perform(
executeFn func(context.Context, []string) error,
) func() error {
return func() error {
s.logger.Printf("Processing things to %s\n", action)
s.Logger.Printf("Processing things to %s\n", action)

thingsToChange := diffFn(things)

// If the changes exceed the maximum change limit, fail with the ErrTooManyChanges error.
if len(thingsToChange) > s.maximumChanges && s.maximumChanges != NoChangeLimit {
return fmt.Errorf("%s(%v) -> %w(%v)", action, thingsToChange, ErrTooManyChanges, s.maximumChanges)
if len(thingsToChange) > s.MaximumChanges && s.MaximumChanges != NoChangeLimit {
return fmt.Errorf("%s(%v) -> %w(%v)", action, thingsToChange, ErrTooManyChanges, s.MaximumChanges)
}

if s.DryRun {
s.logger.Printf("Would %s %s, but running in dry run mode", action, thingsToChange)
s.Logger.Printf("Would %s %s, but running in dry run mode", action, thingsToChange)

return nil
}
Expand All @@ -136,7 +154,7 @@ func (s *Sync) perform(
return nil
}

s.logger.Printf("%s: %s", action, thingsToChange)
s.Logger.Printf("%s: %s", action, thingsToChange)

err := executeFn(ctx, thingsToChange)
if err != nil {
Expand All @@ -149,25 +167,25 @@ func (s *Sync) perform(

// SyncWith synchronises the destination service with the source service, adding & removing things as necessary.
func (s *Sync) SyncWith(ctx context.Context, adapter Adapter) error {
s.logger.Println("Starting sync")
s.Logger.Println("Starting sync")

// Call to populate the cache from the source adapter.
if err := s.generateCache(ctx); err != nil {
return fmt.Errorf("sync.syncwith.generateCache -> %w", err)
}

s.logger.Println("Getting things from destination adapter")
s.Logger.Println("Getting things from destination adapter")

things, err := adapter.Get(ctx)
if err != nil {
return fmt.Errorf("sync.syncwith.get -> %w", err)
}

s.logger.Printf("Running in %s operating mode", s.operatingMode)
s.Logger.Printf("Running in %s operating mode", s.OperatingMode)

operations := make([]func() error, 0, 2) //nolint:gomnd

switch s.operatingMode {
switch s.OperatingMode {
case AddOnly:
operations = []func() error{
s.perform(ctx, "add", things, s.getThingsToAdd, adapter.Add),
Expand Down Expand Up @@ -195,49 +213,7 @@ func (s *Sync) SyncWith(ctx context.Context, adapter Adapter) error {
}
}

s.logger.Println("Finished sync")
s.Logger.Println("Finished sync")

return nil
}

// SetOperatingMode sets a custom operating mode.
func SetOperatingMode(mode OperatingMode) func(*Sync) {
return func(s *Sync) {
s.operatingMode = mode
}
}

// SetCaseSensitive can configure Go Sync's case sensitivity when comparing things.
func SetCaseSensitive(caseSensitive bool) func(*Sync) {
return func(s *Sync) {
s.caseSensitive = caseSensitive
}
}

// SetMaximumChanges sets a maximum number of changes that Sync will allow before returning an ErrTooManyChanges error.
func SetMaximumChanges(maximumChanges int) func(*Sync) {
return func(s *Sync) {
s.maximumChanges = maximumChanges
}
}

// New creates a new gosync.Sync service.
func New(source Adapter, configFns ...func(*Sync)) *Sync {
sync := &Sync{
DryRun: false,

operatingMode: RemoveAdd,
caseSensitive: true,
maximumChanges: NoChangeLimit,
source: source,
cache: make(map[string]bool),

logger: log.New(os.Stderr, "[go-sync/sync] ", log.LstdFlags|log.Lshortfile|log.Lmsgprefix),
}

for _, fn := range configFns {
fn(sync)
}

return sync
}
30 changes: 15 additions & 15 deletions sync_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestNew(t *testing.T) {
syncService := New(adapter)

assert.Empty(t, syncService.cache)
assert.Equal(t, RemoveAdd, syncService.operatingMode)
assert.Equal(t, RemoveAdd, syncService.OperatingMode)
assert.False(t, syncService.DryRun)
assert.Zero(t, adapter.Calls)
}
Expand Down Expand Up @@ -242,7 +242,7 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
assert.NoError(t, err)
})

t.Run("operatingMode", func(t *testing.T) {
t.Run("OperatingMode", func(t *testing.T) {
t.Parallel()

t.Run("AddOnly", func(t *testing.T) {
Expand All @@ -252,7 +252,7 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
destination := NewMockAdapter(t)

syncService := New(source)
syncService.operatingMode = AddOnly
syncService.OperatingMode = AddOnly

source.EXPECT().Get(ctx).Once().Return([]string{"foo"}, nil)
destination.EXPECT().Get(ctx).Once().Return([]string{"bar"}, nil)
Expand All @@ -270,7 +270,7 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
destination := NewMockAdapter(t)

syncService := New(source)
syncService.operatingMode = RemoveOnly
syncService.OperatingMode = RemoveOnly

source.EXPECT().Get(ctx).Once().Return([]string{"foo"}, nil)
destination.EXPECT().Get(ctx).Once().Return([]string{"bar"}, nil)
Expand All @@ -288,7 +288,7 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
destination := NewMockAdapter(t)

syncService := New(source)
syncService.operatingMode = RemoveAdd
syncService.OperatingMode = RemoveAdd

source.EXPECT().Get(ctx).Once().Return([]string{"foo"}, nil)
destination.EXPECT().Get(ctx).Once().Return([]string{"bar"}, nil)
Expand All @@ -310,7 +310,7 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
destination := NewMockAdapter(t)

syncService := New(source)
syncService.operatingMode = AddRemove
syncService.OperatingMode = AddRemove

source.EXPECT().Get(ctx).Once().Return([]string{"foo"}, nil)
destination.EXPECT().Get(ctx).Once().Return([]string{"bar"}, nil)
Expand All @@ -326,7 +326,7 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
})
})

t.Run("caseSensitive", func(t *testing.T) {
t.Run("CaseSensitive", func(t *testing.T) {
t.Parallel()

t.Run("default", func(t *testing.T) {
Expand All @@ -335,7 +335,7 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
source := NewMockAdapter(t)
syncService := New(source)

assert.True(t, syncService.caseSensitive)
assert.True(t, syncService.CaseSensitive)
})

t.Run("true", func(t *testing.T) {
Expand All @@ -350,7 +350,7 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
destination.EXPECT().Remove(ctx, []string{"foo"}).Return(nil)

syncService := New(source)
syncService.caseSensitive = true
syncService.CaseSensitive = true

err := syncService.SyncWith(ctx, destination)

Expand All @@ -368,15 +368,15 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
destination.EXPECT().Add(ctx, []string{"bar"}).Return(nil)

syncService := New(source)
syncService.caseSensitive = false
syncService.CaseSensitive = false

err := syncService.SyncWith(ctx, destination)

assert.NoError(t, err)
})
})

t.Run("maximumChanges", func(t *testing.T) {
t.Run("MaximumChanges", func(t *testing.T) {
t.Parallel()

source := NewMockAdapter(t)
Expand All @@ -391,7 +391,7 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
t.Parallel()

syncService := New(source)
syncService.maximumChanges = 0
syncService.MaximumChanges = 0

err := syncService.SyncWith(ctx, destination)

Expand All @@ -402,14 +402,14 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
t.Parallel()

syncService := New(source)
syncService.maximumChanges = 1
syncService.MaximumChanges = 1

err := syncService.SyncWith(ctx, destination)

assert.ErrorIs(t, err, ErrTooManyChanges)

// Set the operating mode to Remove only (only 1 addition), which should pass successfully.
syncService.operatingMode = RemoveOnly
syncService.OperatingMode = RemoveOnly

err = syncService.SyncWith(ctx, destination)

Expand All @@ -420,7 +420,7 @@ func TestSync_SyncWith(t *testing.T) { //nolint:maintidx
t.Parallel()

syncService := New(source)
syncService.maximumChanges = 2
syncService.MaximumChanges = 2

err := syncService.SyncWith(ctx, destination)

Expand Down
61 changes: 27 additions & 34 deletions sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,41 @@ import (
gosync "github.com/ovotech/go-sync"
)

func ExampleNew() {
// Any Go Sync adapter.
var source gosync.Adapter

gosync.New(source)
type serviceStruct struct {
New func(string) serviceStruct
}

func ExampleSetCaseSensitive() {
// Any Go Sync adapter.
var source gosync.Adapter

gosync.New(source, gosync.SetCaseSensitive(true))
type adapterStruct struct {
Init gosync.InitFn
New func(serviceStruct, string) gosync.Adapter
Token string
Something string
}

func ExampleSetMaximumChanges() {
// Any Go Sync adapter.
var source gosync.Adapter

gosync.New(source, gosync.SetMaximumChanges(5))
}

func ExampleSetOperatingMode() {
// Any Go Sync adapter.
var source gosync.Adapter

// Set the operating mode to add only (don't remove things).
operatingMode := gosync.SetOperatingMode(gosync.AddOnly)

gosync.New(source, operatingMode)
}
//nolint:gochecknoglobals
var (
someAdapter = adapterStruct{}
service = serviceStruct{}
)

func ExampleSync_SyncWith() {
// Any Go Sync adapters.
var source, destination gosync.Adapter
func ExampleNew() {
// Create an adapter using the recommended New method.
client := service.New("some-token")
source := someAdapter.New(client, "some-value")

// Initialise an adapter using an Init function.
destination, err := someAdapter.Init(context.Background(), map[gosync.ConfigKey]string{
someAdapter.Token: "some-token",
someAdapter.Something: "some-value",
})
if err != nil {
log.Fatal(err)
}

sync := gosync.New(source)

// By default, Go Sync runs in dry run mode. To make changes this must manually be set to false.
sync.DryRun = false

err := sync.SyncWith(context.Background(), destination)
err = sync.SyncWith(context.Background(), destination)
if err != nil {
log.Panic(err)
log.Fatal(err)
}
}

0 comments on commit c245f90

Please sign in to comment.