mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 00:49:03 +00:00
Calendar interface fixes from code review and refactoring. (#17658)
Calendar interface fixes from code review and manual merge with @lucasmrod changes.
This commit is contained in:
parent
2db8eb3c80
commit
21f95d8b5d
3 changed files with 50 additions and 63 deletions
|
|
@ -1,16 +0,0 @@
|
|||
package calendar
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
type GoogleCalendarConfig struct {
|
||||
Context context.Context
|
||||
IntegrationConfig *fleet.GoogleCalendarIntegration
|
||||
UserEmail string
|
||||
Logger log.Logger
|
||||
// Should be nil for production
|
||||
API GoogleCalendarAPI
|
||||
}
|
||||
|
|
@ -5,16 +5,18 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
kitlog "github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"golang.org/x/oauth2/google"
|
||||
"golang.org/x/oauth2/jwt"
|
||||
"google.golang.org/api/calendar/v3"
|
||||
"google.golang.org/api/googleapi"
|
||||
"google.golang.org/api/option"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -30,15 +32,34 @@ var calendarScopes = []string{
|
|||
"https://www.googleapis.com/auth/calendar.settings.readonly",
|
||||
}
|
||||
|
||||
// GoogleCalendar is an implementation of the Calendar interface that uses the
|
||||
type GoogleCalendarConfig struct {
|
||||
Context context.Context
|
||||
IntegrationConfig *fleet.GoogleCalendarIntegration
|
||||
Logger kitlog.Logger
|
||||
// Should be nil for production
|
||||
API GoogleCalendarAPI
|
||||
}
|
||||
|
||||
// GoogleCalendar is an implementation of the UserCalendar interface that uses the
|
||||
// Google Calendar API to manage events.
|
||||
type GoogleCalendar struct {
|
||||
config *GoogleCalendarConfig
|
||||
timezoneOffset *int
|
||||
config *GoogleCalendarConfig
|
||||
currentUserEmail string
|
||||
timezoneOffset *int
|
||||
}
|
||||
|
||||
func NewGoogleCalendar(config *GoogleCalendarConfig) *GoogleCalendar {
|
||||
if config.API == nil {
|
||||
var lowLevelAPI GoogleCalendarAPI = &GoogleCalendarLowLevelAPI{}
|
||||
config.API = lowLevelAPI
|
||||
}
|
||||
return &GoogleCalendar{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
type GoogleCalendarAPI interface {
|
||||
Connect(ctx context.Context, email, privateKey, subject string) error
|
||||
Configure(ctx context.Context, serviceAccountEmail, privateKey, userToImpersonateEmail string) error
|
||||
GetSetting(name string) (*calendar.Setting, error)
|
||||
ListEvents(timeMin, timeMax string) (*calendar.Events, error)
|
||||
CreateEvent(event *calendar.Event) (*calendar.Event, error)
|
||||
|
|
@ -55,15 +76,17 @@ type GoogleCalendarLowLevelAPI struct {
|
|||
service *calendar.Service
|
||||
}
|
||||
|
||||
// Connect creates a new Google Calendar service using the provided credentials.
|
||||
func (lowLevelAPI *GoogleCalendarLowLevelAPI) Connect(ctx context.Context, email, privateKey, subject string) error {
|
||||
// Configure creates a new Google Calendar service using the provided credentials.
|
||||
func (lowLevelAPI *GoogleCalendarLowLevelAPI) Configure(
|
||||
ctx context.Context, serviceAccountEmail, privateKey, userToImpersonateEmail string,
|
||||
) error {
|
||||
// Create a new calendar service
|
||||
conf := &jwt.Config{
|
||||
Email: email,
|
||||
Email: serviceAccountEmail,
|
||||
Scopes: calendarScopes,
|
||||
PrivateKey: []byte(privateKey),
|
||||
TokenURL: google.JWTTokenURL,
|
||||
Subject: subject,
|
||||
Subject: userToImpersonateEmail,
|
||||
}
|
||||
client := conf.Client(ctx)
|
||||
service, err := calendar.NewService(ctx, option.WithHTTPClient(client))
|
||||
|
|
@ -95,33 +118,18 @@ func (lowLevelAPI *GoogleCalendarLowLevelAPI) DeleteEvent(id string) error {
|
|||
return lowLevelAPI.service.Events.Delete(calendarID, id).Do()
|
||||
}
|
||||
|
||||
func (c *GoogleCalendar) Connect(config any) (fleet.Calendar, error) {
|
||||
gConfig, ok := config.(*GoogleCalendarConfig)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid Google calendar config")
|
||||
}
|
||||
if gConfig.API == nil {
|
||||
var lowLevelAPI GoogleCalendarAPI = &GoogleCalendarLowLevelAPI{}
|
||||
gConfig.API = lowLevelAPI
|
||||
}
|
||||
err := gConfig.API.Connect(
|
||||
gConfig.Context, gConfig.IntegrationConfig.Email, gConfig.IntegrationConfig.PrivateKey, gConfig.UserEmail,
|
||||
func (c *GoogleCalendar) Configure(userEmail string) error {
|
||||
err := c.config.API.Configure(
|
||||
c.config.Context, c.config.IntegrationConfig.Email, c.config.IntegrationConfig.PrivateKey, userEmail,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(gConfig.Context, err, "creating Google calendar service")
|
||||
return ctxerr.Wrap(c.config.Context, err, "creating Google calendar service")
|
||||
}
|
||||
|
||||
gCal := &GoogleCalendar{
|
||||
config: gConfig,
|
||||
}
|
||||
|
||||
return gCal, nil
|
||||
c.currentUserEmail = userEmail
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GoogleCalendar) GetAndUpdateEvent(event *fleet.CalendarEvent, genBodyFn func() string) (*fleet.CalendarEvent, bool, error) {
|
||||
if c.config == nil {
|
||||
return nil, false, errors.New("the Google calendar is not connected. Please call Connect first")
|
||||
}
|
||||
if event.EndTime.Before(time.Now()) {
|
||||
return nil, false, ctxerr.Errorf(c.config.Context, "cannot get and update an event that has already ended: %s", event.EndTime)
|
||||
}
|
||||
|
|
@ -199,16 +207,11 @@ func (c *GoogleCalendar) unmarshalDetails(event *fleet.CalendarEvent) (*eventDet
|
|||
if details.ID == "" {
|
||||
return nil, ctxerr.Errorf(c.config.Context, "missing Google calendar event ID")
|
||||
}
|
||||
if details.ETag == "" {
|
||||
return nil, ctxerr.Errorf(c.config.Context, "missing Google calendar event ETag")
|
||||
}
|
||||
// ETag is optional, but we need it to check if the event was modified
|
||||
return &details, nil
|
||||
}
|
||||
|
||||
func (c *GoogleCalendar) CreateEvent(dayOfEvent time.Time, body string) (*fleet.CalendarEvent, error) {
|
||||
if c.config == nil {
|
||||
return nil, errors.New("the Google calendar is not connected. Please call Connect first")
|
||||
}
|
||||
if c.timezoneOffset == nil {
|
||||
err := getTimezone(c)
|
||||
if err != nil {
|
||||
|
|
@ -257,7 +260,7 @@ func (c *GoogleCalendar) CreateEvent(dayOfEvent time.Time, body string) (*fleet.
|
|||
attending = true
|
||||
} else {
|
||||
for _, attendee := range gEvent.Attendees {
|
||||
if attendee.Email == c.config.UserEmail {
|
||||
if attendee.Email == c.currentUserEmail {
|
||||
if attendee.ResponseStatus != "declined" {
|
||||
attending = true
|
||||
}
|
||||
|
|
@ -316,8 +319,7 @@ func (c *GoogleCalendar) CreateEvent(dayOfEvent time.Time, body string) (*fleet.
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
level.Debug(c.config.Logger).Log("msg", "created Google calendar events", "user", c.config.UserEmail, "startTime", eventStart)
|
||||
fmt.Printf("VICTOR Created event with id:%s and ETag:%s\n", event.Id, event.Etag)
|
||||
level.Debug(c.config.Logger).Log("msg", "created Google calendar event", "user", c.currentUserEmail, "startTime", eventStart)
|
||||
|
||||
return fleetEvent, nil
|
||||
}
|
||||
|
|
@ -364,7 +366,7 @@ func (c *GoogleCalendar) googleEventToFleetEvent(startTime time.Time, endTime ti
|
|||
fleetEvent := &fleet.CalendarEvent{}
|
||||
fleetEvent.StartTime = startTime
|
||||
fleetEvent.EndTime = endTime
|
||||
fleetEvent.Email = c.config.UserEmail
|
||||
fleetEvent.Email = c.currentUserEmail
|
||||
details := &eventDetails{
|
||||
ID: event.Id,
|
||||
ETag: event.Etag,
|
||||
|
|
@ -379,7 +381,7 @@ func (c *GoogleCalendar) googleEventToFleetEvent(startTime time.Time, endTime ti
|
|||
|
||||
func (c *GoogleCalendar) DeleteEvent(event *fleet.CalendarEvent) error {
|
||||
if c.config == nil {
|
||||
return errors.New("the Google calendar is not connected. Please call Connect first")
|
||||
return errors.New("the Google calendar is not connected. Please call Configure first")
|
||||
}
|
||||
details, err := c.unmarshalDetails(event)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -10,15 +10,16 @@ func (e DayEndedError) Error() string {
|
|||
return e.Msg
|
||||
}
|
||||
|
||||
type Calendar interface {
|
||||
// Connect to calendar. This method must be called first. Currently, config must be a *GoogleCalendarConfig
|
||||
Connect(config any) (Calendar, error)
|
||||
type UserCalendar interface {
|
||||
// Configure configures the connection to a user's calendar. Once configured,
|
||||
// CreateEvent, GetAndUpdateEvent and DeleteEvent reference the user's calendar.
|
||||
Configure(userEmail string) error
|
||||
// CreateEvent creates a new event on the calendar on the given date. DayEndedError is returned if there is no time left on the given date to schedule event.
|
||||
CreateEvent(dateOfEvent time.Time, body string) (event *CalendarEvent, err error)
|
||||
// GetAndUpdateEvent retrieves the event from the calendar.
|
||||
// If the event has been modified, it returns the updated event.
|
||||
// If the event has been deleted, it schedules a new event with given body callback and returns the new event.
|
||||
GetAndUpdateEvent(event *CalendarEvent, genBodyFn func() string) (updatedEvent *CalendarEvent, updated bool, err error)
|
||||
// CreateEvent creates a new event on the calendar on the given date. DayEndedError is returned if there is no time left on the given date to schedule event.
|
||||
CreateEvent(dateOfEvent time.Time, body string) (event *CalendarEvent, err error)
|
||||
// DeleteEvent deletes the event with the given ID.
|
||||
DeleteEvent(event *CalendarEvent) error
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue