diff --git a/example.sync.yaml b/example.sync.yaml index 1dfaadc..d0bdc54 100644 --- a/example.sync.yaml +++ b/example.sync.yaml @@ -25,7 +25,7 @@ source: tenantId: "[UUID-format string here]" # ZEP source adapter -#source: +#source: # adapter: # type: "zep" # calendar: "absences" @@ -60,6 +60,10 @@ filters: - name: DeclinedEvents # Events which cover the full day aren't synced - name: AllDayEvents + # Exclude Events which match the following regular expression, syntax here: https://github.com/google/re2/wiki/Syntax + - name: RegexTitle + config: + ExcludeRegexp: ".*test" # Perform multiple calendar updates concurrently # Defaults to 1 if not set diff --git a/internal/filter/allDayEvents_test.go b/internal/filter/allDayEvents_test.go new file mode 100644 index 0000000..341959c --- /dev/null +++ b/internal/filter/allDayEvents_test.go @@ -0,0 +1,39 @@ +package filter_test + +import ( + "testing" + + "github.com/inovex/CalendarSync/internal/filter" + "github.com/inovex/CalendarSync/internal/models" +) + +// All Day Events should be filtered +func TestAllDayEventsFilter(t *testing.T) { + sourceEvents := []models.Event{ + { + ICalUID: "testId", + ID: "testUid", + Title: "test", + Description: "bar", + AllDay: true, + }, + { + ICalUID: "testId2", + ID: "testUid2", + Title: "Test 2", + Description: "bar", + AllDay: false, + }, + { + ICalUID: "testId3", + ID: "testUid2", + Title: "foo", + Description: "bar", + }, + } + + expectedSinkEvents := []models.Event{sourceEvents[1], sourceEvents[2]} + + eventFilter := filter.AllDayEvents{} + checkEventFilter(t, eventFilter, sourceEvents, expectedSinkEvents) +} diff --git a/internal/filter/declinedEvents_test.go b/internal/filter/declinedEvents_test.go new file mode 100644 index 0000000..54c81fe --- /dev/null +++ b/internal/filter/declinedEvents_test.go @@ -0,0 +1,40 @@ +package filter_test + +import ( + "testing" + + "github.com/inovex/CalendarSync/internal/filter" + "github.com/inovex/CalendarSync/internal/models" +) + +// Declined Events should be filtered +func TestDeclinedEventsFilter(t *testing.T) { + sourceEvents := []models.Event{ + { + ICalUID: "testId", + ID: "testUid", + Title: "test", + Description: "bar", + Accepted: false, + }, + { + ICalUID: "testId2", + ID: "testUid2", + Title: "Test 2", + Description: "bar", + Accepted: true, + }, + { + ICalUID: "testId3", + ID: "testUid3", + Title: "foo", + Description: "bar", + Accepted: true, + }, + } + + expectedSinkEvents := []models.Event{sourceEvents[1], sourceEvents[2]} + + eventFilter := filter.DeclinedEvents{} + checkEventFilter(t, eventFilter, sourceEvents, expectedSinkEvents) +} diff --git a/internal/filter/regexTitle.go b/internal/filter/regexTitle.go new file mode 100644 index 0000000..ad23b78 --- /dev/null +++ b/internal/filter/regexTitle.go @@ -0,0 +1,40 @@ +package filter + +import ( + "regexp" + + "github.com/charmbracelet/log" + + "github.com/inovex/CalendarSync/internal/models" +) + +type RegexTitle struct { + ExcludeRegexp string +} + +func (a RegexTitle) Name() string { + return "RegexTitle" +} + +func (a RegexTitle) Filter(event models.Event) bool { + + if len(a.ExcludeRegexp) == 0 { + log.Debugf("Regular Expression is empty, skipping Filter %s for event: %s", a.Name(), event.Title) + return true + } + + log.Debugf("Running Regexp %s on event title: %s", a.ExcludeRegexp, event.Title) + + r, err := regexp.Compile(a.ExcludeRegexp) + if err != nil { + log.Fatalf("Regular expression of Filter %s is not valid, please check", a.Name()) + } + + // if the title matches the Regexp, return false (filter the event) + if r.MatchString(event.Title) { + log.Debugf("Regular Expression %s matches the events title: %s, gets filtered", a.Name(), event.Title) + return false + } + + return true +} diff --git a/internal/filter/regexTitle_test.go b/internal/filter/regexTitle_test.go new file mode 100644 index 0000000..03ae28d --- /dev/null +++ b/internal/filter/regexTitle_test.go @@ -0,0 +1,50 @@ +package filter_test + +import ( + "testing" + + "github.com/inovex/CalendarSync/internal/filter" + "github.com/inovex/CalendarSync/internal/models" +) + +var sourceEvents = []models.Event{ + { + ICalUID: "testId", + ID: "testUid", + Title: "test", + Description: "bar", + }, + { + ICalUID: "testId2", + ID: "testUid2", + Title: "Test 2", + Description: "bar", + }, + { + ICalUID: "testId3", + ID: "testUid2", + Title: "foo", + Description: "bar", + }, +} + +// Some events should be filtered +func TestRegexTitleFilter(t *testing.T) { + + expectedSinkEvents := []models.Event{sourceEvents[1], sourceEvents[2]} + + eventFilter := filter.RegexTitle{ + ExcludeRegexp: ".*test", + } + checkEventFilter(t, eventFilter, sourceEvents, expectedSinkEvents) +} + +// All Events should be there +func TestRegexTitleFilterEmptyRegex(t *testing.T) { + expectedSinkEvents := sourceEvents + + eventFilter := filter.RegexTitle{ + ExcludeRegexp: "", + } + checkEventFilter(t, eventFilter, sourceEvents, expectedSinkEvents) +} diff --git a/internal/filter/util_test.go b/internal/filter/util_test.go new file mode 100644 index 0000000..924064d --- /dev/null +++ b/internal/filter/util_test.go @@ -0,0 +1,23 @@ +package filter_test + +import ( + "github.com/inovex/CalendarSync/internal/models" + "github.com/inovex/CalendarSync/internal/sync" + "github.com/stretchr/testify/assert" +) + +// FilterEvents takes an array of events and a filter and executes the .Filter Method on each of the sourceEvents +// Not excluded events get returned in the filteredEvents +func FilterEvents(sourceEvents []models.Event, filter sync.Filter) (filteredEvents []models.Event) { + for _, event := range sourceEvents { + if filter.Filter(event) { + filteredEvents = append(filteredEvents, event) + } + } + return filteredEvents +} + +func checkEventFilter(t assert.TestingT, filter sync.Filter, sourceEvents []models.Event, expectedEvents []models.Event) { + filteredEvents := FilterEvents(sourceEvents, filter) + assert.Equal(t, expectedEvents, filteredEvents) +} diff --git a/internal/sync/filter.go b/internal/sync/filter.go index 7004e9a..3222995 100644 --- a/internal/sync/filter.go +++ b/internal/sync/filter.go @@ -1,9 +1,10 @@ package sync import ( - "fmt" "strings" + "github.com/charmbracelet/log" + "github.com/inovex/CalendarSync/internal/config" "github.com/inovex/CalendarSync/internal/filter" "github.com/inovex/CalendarSync/internal/models" @@ -31,19 +32,21 @@ var ( filterConfigMapping = map[string]Filter{ "DeclinedEvents": &filter.DeclinedEvents{}, "AllDayEvents": &filter.AllDayEvents{}, + "RegexTitle": &filter.RegexTitle{}, } filterOrder = []string{ "DeclinedEvents", "AllDayEvents", + "RegexTitle", } ) func FilterFactory(configuredFilters []config.Filter) (loadedFilters []Filter) { for _, configuredFilter := range configuredFilters { if _, nameExists := filterConfigMapping[configuredFilter.Name]; !nameExists { - // todo: handle properly - panic(fmt.Sprintf("unknown filter: %s", configuredFilter.Name)) + log.Warnf("unknown filter: %s, skipping...", configuredFilter.Name) + continue } // load the default Transformer for the configured name and initialize it based on the config filterDefault := filterConfigMapping[configuredFilter.Name] diff --git a/internal/sync/transformer.go b/internal/sync/transformer.go index 34eb567..a9864fd 100644 --- a/internal/sync/transformer.go +++ b/internal/sync/transformer.go @@ -1,9 +1,10 @@ package sync import ( - "fmt" "strings" + "github.com/charmbracelet/log" + "github.com/inovex/CalendarSync/internal/config" "github.com/inovex/CalendarSync/internal/models" "github.com/inovex/CalendarSync/internal/transformation" @@ -58,8 +59,8 @@ var ( func TransformerFactory(configuredTransformers []config.Transformer) (loadedTransformers []Transformer) { for _, configuredTransformer := range configuredTransformers { if _, nameExists := transformerConfigMapping[configuredTransformer.Name]; !nameExists { - // todo: handle properly - panic(fmt.Sprintf("unknown transformer: %s", configuredTransformer.Name)) + log.Warnf("unknown transformer: %s, skipping...", configuredTransformer.Name) + continue } // load the default Transformer for the configured name and initialize it based on the config transformerDefault := transformerConfigMapping[configuredTransformer.Name]