Skip to content

Commit

Permalink
Merge pull request #62 from inovex/sync-declined-events
Browse files Browse the repository at this point in the history
feat: Implement option to enable / disable sync of declined events
  • Loading branch information
Alexander Huck authored Oct 4, 2023
2 parents 74f8dc3 + 45de524 commit 924a56d
Show file tree
Hide file tree
Showing 21 changed files with 289 additions and 56 deletions.
1 change: 1 addition & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: Build
on: [ push ]

env:
GO_VERSION: '1.20.4'
GO_VERSION_SHORT: '1.20'
GOLANGCI_VERSION: 'v1.52.2'
GO_VERSION: '1.21.0'
GO_VERSION_SHORT: '1.21'
GOLANGCI_VERSION: 'v1.54.0'
STATICCHECK_VERSION: '2023.1.3'

permissions:
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ on:
tags:
- 'v*'

env:
GO_VERSION: '1.21.0'

permissions:
contents: write
packages: write
Expand All @@ -19,6 +22,8 @@ jobs:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}

- name: Log in to GitHub container registry
uses: docker/login-action@v3
Expand Down
1 change: 1 addition & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
before:
hooks:
- go mod tidy
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,21 @@ transformations:
The transformers are applied in a specific order. The order is defined here:
[`internal/sync/transformer.go`](./internal/sync/transformer.go)

## Filters

In some cases events should not be synced. For example, declined events might
create too much noise in the target calendar. These can be filtered by enabling
the corresponding filter.

```yaml
# Filters remove events from being synced due to different criteria
filters:
# Events where you declined the invitation aren't synced
- name: DeclinedEvents
# Events which cover the full day aren't synced
- name: AllDayEvents
```

# Cleaning Up

You just synced a lot of events in your calendar and decide you want to use a
Expand Down
2 changes: 1 addition & 1 deletion cmd/calendarsync/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func Run(c *cli.Context) error {
}
}

controller := sync.NewController(log.Default(), sourceAdapter, sinkAdapter, sync.TransformerFactory(cfg.Transformations)...)
controller := sync.NewController(log.Default(), sourceAdapter, sinkAdapter, sync.TransformerFactory(cfg.Transformations), sync.FilterFactory(cfg.Filters))
if cfg.UpdateConcurrency != 0 {
controller.SetConcurrency(cfg.UpdateConcurrency)
}
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
version: '3.4'

services:
Expand Down
7 changes: 7 additions & 0 deletions example.sync.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ transformations:
config:
UseEmailAsDisplayName: true

# Filters remove events from being synced due to different criteria
filters:
# Events where you declined the invitation aren't synced
- name: DeclinedEvents
# Events which cover the full day aren't synced
- name: AllDayEvents

# Perform multiple calendar updates concurrently
# Defaults to 1 if not set
updateConcurrency: 3
5 changes: 5 additions & 0 deletions internal/adapter/google/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ func calendarEventToEvent(e *calendar.Event, adapterSourceID string) models.Even
metadata := ensureMetadata(e, adapterSourceID)

var attendees []models.Attendee
var hasEventAccepted bool = true
for _, eventAttendee := range e.Attendees {
if eventAttendee.Self && eventAttendee.ResponseStatus == "declined" {
hasEventAccepted = false
}
attendees = append(attendees, models.Attendee{
Email: eventAttendee.Email,
DisplayName: eventAttendee.DisplayName,
Expand Down Expand Up @@ -47,6 +51,7 @@ func calendarEventToEvent(e *calendar.Event, adapterSourceID string) models.Even
Attendees: attendees,
Reminders: reminders,
MeetingLink: e.HangoutLink,
Accepted: hasEventAccepted,
}
}

Expand Down
5 changes: 5 additions & 0 deletions internal/adapter/outlook_http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@ func (o OutlookClient) outlookEventToEvent(oe Event, adapterSourceID string) (e
},
})
}
var hasEventAccepted bool = true
if oe.ResponseStatus.Response == "declined" {
hasEventAccepted = false
}

bufEvent = models.Event{
ICalUID: oe.UID,
Expand All @@ -280,6 +284,7 @@ func (o OutlookClient) outlookEventToEvent(oe Event, adapterSourceID string) (e
Attendees: attendees,
Reminders: reminders,
MeetingLink: oe.OnlineMeetingUrl,
Accepted: hasEventAccepted,
}

if oe.IsAllDay {
Expand Down
37 changes: 22 additions & 15 deletions internal/adapter/outlook_http/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,22 @@ type EventList struct {
}

type Event struct {
ID string `json:"id"`
UID string `json:"iCalUId"`
ChangeKey string `json:"changeKey"`
HtmlLink string `json:"webLink"`
Subject string `json:"subject"`
Start Time `json:"start"`
End Time `json:"end"`
Body Body `json:"body,omitempty"`
Attendees []Attendee `json:"attendees,omitempty"`
Location Location `json:"location"`
IsReminderOn bool `json:"isReminderOn"`
ReminderMinutesBeforeStart int `json:"reminderMinutesBeforeStart"`
Extensions []Extensions `json:"extensions"`
IsAllDay bool `json:"isAllDay"`
OnlineMeetingUrl string `json:"onlineMeetingUrl"`
ID string `json:"id"`
UID string `json:"iCalUId"`
ChangeKey string `json:"changeKey"`
HtmlLink string `json:"webLink"`
Subject string `json:"subject"`
Start Time `json:"start"`
End Time `json:"end"`
Body Body `json:"body,omitempty"`
Attendees []Attendee `json:"attendees,omitempty"`
Location Location `json:"location"`
IsReminderOn bool `json:"isReminderOn"`
ReminderMinutesBeforeStart int `json:"reminderMinutesBeforeStart"`
Extensions []Extensions `json:"extensions"`
IsAllDay bool `json:"isAllDay"`
OnlineMeetingUrl string `json:"onlineMeetingUrl"`
ResponseStatus ResponseStatus `json:"responseStatus,omitempty"`
}

type Extensions struct {
Expand All @@ -36,6 +37,12 @@ type Extensions struct {
models.Metadata
}

type ResponseStatus struct {
Response string `json:"response,omitempty"`
// there's an additional field called `time` which returns date and time when the response was returned
// but we don't need that
}

type Body struct {
ContentType string `json:"contentType,omitempty"`
Content string `json:"content,omitempty"`
Expand Down
17 changes: 12 additions & 5 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (
)

type File struct {
Path string
Auth AuthStorage
Source Source `yaml:"source"`
Sink Sink `yaml:"sink"`
// TODO: filters for source events
Path string
Auth AuthStorage
Source Source `yaml:"source"`
Sink Sink `yaml:"sink"`
Filters []Filter `yaml:"filters,omitempty"`
Transformations []Transformer `yaml:"transformations,omitempty"`
Sync Sync `yaml:"sync"`
UpdateConcurrency int `yaml:"updateConcurrency,omitempty"`
Expand Down Expand Up @@ -83,6 +83,13 @@ type Transformer struct {
Config CustomMap `yaml:"config"`
}

type Filter struct {
// Name of the filter
Name string `yaml:"name"`
// Any kind of parameter which can be passed to a filter.
Config CustomMap `yaml:"config"`
}

// Sync configuration
type Sync struct {
StartTime SyncTime `yaml:"start"`
Expand Down
16 changes: 16 additions & 0 deletions internal/filter/allDayEvents.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package filter

import (
"github.com/inovex/CalendarSync/internal/models"
)

type AllDayEvents struct {
}

func (a AllDayEvents) Name() string {
return "AllDayEvents"
}

func (a AllDayEvents) Filter(event models.Event) bool {
return !event.AllDay
}
15 changes: 15 additions & 0 deletions internal/filter/declinedEvents.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package filter

import (
"github.com/inovex/CalendarSync/internal/models"
)

type DeclinedEvents struct{}

func (d DeclinedEvents) Name() string {
return "DeclinedEvents"
}

func (d DeclinedEvents) Filter(event models.Event) bool {
return event.Accepted
}
1 change: 1 addition & 0 deletions internal/models/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Event struct {
Attendees Attendees
Reminders Reminders
MeetingLink string
Accepted bool
}

type Reminders []Reminder
Expand Down
20 changes: 18 additions & 2 deletions internal/sync/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@ type Controller struct {
source Source
// transformers are applied in order
transformers []Transformer
filters []Filter
sink Sink
concurrency int
logger *log.Logger
}

// NewController constructs a new Controller.
func NewController(logger *log.Logger, source Source, sink Sink, transformer ...Transformer) Controller {
func NewController(logger *log.Logger, source Source, sink Sink, transformer []Transformer, filters []Filter) Controller {
return Controller{
concurrency: 1,
source: source,
transformers: transformer,
filters: filters,
sink: sink,
logger: logger,
}
Expand Down Expand Up @@ -75,6 +77,20 @@ func (p Controller) SynchroniseTimeframe(ctx context.Context, start time.Time, e
return err
}

filteredEventsInSource := []models.Event{}

for _, filter := range p.filters {
p.logger.Info("loaded filter", "name", filter.Name())
}

for _, event := range eventsInSource {
if FilterEvent(event, p.filters...) {
filteredEventsInSource = append(filteredEventsInSource, event)
} else {
p.logger.Debug("filter rejects event", logFields(event)...)
}
}

// Transform source events before comparing them to the sink events
transformedEventsInSource := []models.Event{}

Expand All @@ -83,7 +99,7 @@ func (p Controller) SynchroniseTimeframe(ctx context.Context, start time.Time, e
p.logger.Info("loaded transformer", "name", trans.Name())
}

for _, event := range eventsInSource {
for _, event := range filteredEventsInSource {
transformedEventsInSource = append(transformedEventsInSource, TransformEvent(event, p.transformers...))
}

Expand Down
Loading

0 comments on commit 924a56d

Please sign in to comment.