Fixed issue where callback was clearing event body tag. (#20997)

#20994 unreleased bug -- callback was clearing event body tag

# Checklist for submitter

- [x] Added/updated tests
- [x] Manual QA for all new/changed functionality
This commit is contained in:
Victor Lyuboslavsky
2024-08-05 13:44:30 -03:00
committed by GitHub
parent 8362c328a1
commit 40b0349d13
7 changed files with 72 additions and 26 deletions
+14 -13
View File
@@ -298,30 +298,30 @@ func (c *GoogleCalendar) Configure(userEmail string) error {
}
func (c *GoogleCalendar) UpdateEventBody(event *fleet.CalendarEvent,
genBodyFn fleet.CalendarGenBodyFn) error {
genBodyFn fleet.CalendarGenBodyFn) (string, error) {
details, err := c.unmarshalDetails(event)
if err != nil {
return err
return "", err
}
gEvent, err := c.config.API.GetEvent(details.ID, "")
if err != nil {
return ctxerr.Wrap(c.config.Context, err, "retrieving Google calendar event")
return "", ctxerr.Wrap(c.config.Context, err, "retrieving Google calendar event")
}
// Check if the current description contains the conflict text
conflict := strings.Contains(gEvent.Description, fleet.CalendarEventConflictText)
var ok bool
gEvent.Description, ok, err = genBodyFn(conflict)
if err != nil {
return ctxerr.Wrap(c.config.Context, err, "generating calendar event body")
return "", ctxerr.Wrap(c.config.Context, err, "generating calendar event body")
}
if !ok {
return nil
return "", nil
}
_, err = c.config.API.UpdateEvent(gEvent)
updatedEvent, err := c.config.API.UpdateEvent(gEvent)
if err != nil {
return ctxerr.Wrap(c.config.Context, err, "updating Google calendar event")
return "", ctxerr.Wrap(c.config.Context, err, "updating Google calendar event")
}
return nil
return updatedEvent.Etag, nil
}
func (c *GoogleCalendar) GetAndUpdateEvent(event *fleet.CalendarEvent, genBodyFn fleet.CalendarGenBodyFn,
@@ -441,7 +441,8 @@ func (c *GoogleCalendar) GetAndUpdateEvent(event *fleet.CalendarEvent, genBodyFn
if err != nil {
return nil, false, err
}
fleetEvent, err := c.googleEventToFleetEvent(*startTime, *endTime, gEvent, event.UUID, details.ChannelID, details.ResourceID)
fleetEvent, err := c.googleEventToFleetEvent(*startTime, *endTime, gEvent, event.UUID, details.ChannelID, details.ResourceID,
details.BodyTag)
if err != nil {
return nil, false, err
}
@@ -674,8 +675,8 @@ func (c *GoogleCalendar) createEvent(
resourceID = opts.ResourceID
}
// Convert Google event to Fleet event
fleetEvent, err := c.googleEventToFleetEvent(eventStart, eventEnd, event, eventUUID, channelID, resourceID)
// Convert Google event to Fleet event. Body tag will be updated by the calling function.
fleetEvent, err := c.googleEventToFleetEvent(eventStart, eventEnd, event, eventUUID, channelID, resourceID, "body_tag")
if err != nil {
return nil, err
}
@@ -726,8 +727,7 @@ func getLocation(tz string, config *GoogleCalendarConfig) *time.Location {
}
func (c *GoogleCalendar) googleEventToFleetEvent(startTime time.Time, endTime time.Time, event *calendar.Event, eventUUID string,
channelID string,
resourceID string) (
channelID string, resourceID string, bodyTag string) (
*fleet.CalendarEvent, error,
) {
tzName := c.location.String()
@@ -742,6 +742,7 @@ func (c *GoogleCalendar) googleEventToFleetEvent(startTime time.Time, endTime ti
ETag: event.Etag,
ChannelID: channelID,
ResourceID: resourceID,
BodyTag: bodyTag,
}
detailsJson, err := json.Marshal(details)
if err != nil {
+3 -1
View File
@@ -235,7 +235,9 @@ func (svc *Service) processCalendarEvent(ctx context.Context, eventDetails *flee
return ctxerr.Wrap(ctx, err, "set recent update flag")
}
// Event was updated, so we need to save it
err = event.SaveBodyTag(generatedTag)
if generatedTag != "" {
err = event.SaveDataItems("body_tag", generatedTag)
}
if err != nil {
return ctxerr.Wrap(ctx, err, "save calendar event body tag")
}
+5 -4
View File
@@ -396,6 +396,7 @@ func processFailingHostExistingCalendarEvent(
// Function to generate calendar event body.
var generatedTag string
var newETag string
var genBodyFn fleet.CalendarGenBodyFn = func(conflict bool) (string, bool, error) {
var body string
body, generatedTag = calendar.GenerateCalendarEventBody(ctx, ds, orgName, host, policyIDtoPolicy, conflict, logger)
@@ -409,7 +410,7 @@ func processFailingHostExistingCalendarEvent(
updatedBodyTag := getBodyTag(ctx, ds, host, policyIDtoPolicy, logger)
if currentBodyTag != updatedBodyTag && updatedBodyTag != "" {
err = userCalendar.UpdateEventBody(calendarEvent, genBodyFn)
newETag, err = userCalendar.UpdateEventBody(calendarEvent, genBodyFn)
if err != nil {
return fmt.Errorf("update event body: %w", err)
}
@@ -440,8 +441,8 @@ func processFailingHostExistingCalendarEvent(
}
if updated {
if generatedTag != "" {
err = updatedEvent.SaveBodyTag(generatedTag)
if generatedTag != "" && newETag != "" {
err = updatedEvent.SaveDataItems("body_tag", generatedTag, "etag", newETag)
if err != nil {
return fmt.Errorf("save calendar event body tag: %w", err)
}
@@ -623,7 +624,7 @@ func attemptCreatingEventOnUserCalendar(
var dee fleet.DayEndedError
switch {
case err == nil:
err = calendarEvent.SaveBodyTag(generatedTag)
err = calendarEvent.SaveDataItems("body_tag", generatedTag)
if err != nil {
return nil, err
}
+2 -2
View File
@@ -42,8 +42,8 @@ type UserCalendar interface {
GetAndUpdateEvent(event *CalendarEvent, genBodyFn CalendarGenBodyFn,
opts CalendarGetAndUpdateEventOpts) (updatedEvent *CalendarEvent,
updated bool, err error)
// UpdateEventBody updates the body of the calendar event.
UpdateEventBody(event *CalendarEvent, genBodyFn CalendarGenBodyFn) error
// UpdateEventBody updates the body of the calendar event and returns new ETag
UpdateEventBody(event *CalendarEvent, genBodyFn CalendarGenBodyFn) (string, error)
// DeleteEvent deletes the event with the given ID.
DeleteEvent(event *CalendarEvent) error
// StopEventChannel stops the event's callback channel.
+10 -2
View File
@@ -2,6 +2,7 @@ package fleet
import (
"encoding/json"
"errors"
"fmt"
"time"
)
@@ -30,7 +31,10 @@ func (ce *CalendarEvent) GetBodyTag() string {
return d.BodyTag
}
func (ce *CalendarEvent) SaveBodyTag(bodyTag string) error {
func (ce *CalendarEvent) SaveDataItems(keysAndValues ...string) error {
if len(keysAndValues)%2 != 0 {
return errors.New("SaveDataItem requires an even number of arguments")
}
var result map[string]any
if len(ce.Data) > 0 {
err := json.Unmarshal(ce.Data, &result)
@@ -40,7 +44,11 @@ func (ce *CalendarEvent) SaveBodyTag(bodyTag string) error {
} else {
result = make(map[string]any, 1)
}
result["body_tag"] = bodyTag
for i := 0; i < len(keysAndValues); i += 2 {
key := keysAndValues[i]
value := keysAndValues[i+1]
result[key] = value
}
data, err := json.Marshal(result)
if err != nil {
return fmt.Errorf("could not marshal event data: %w", err)
+4 -4
View File
@@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/require"
)
func TestBodyTag(t *testing.T) {
func TestSaveDataItems(t *testing.T) {
t.Parallel()
var event CalendarEvent
@@ -16,7 +16,7 @@ func TestBodyTag(t *testing.T) {
assert.Equal(t, "", event.GetBodyTag())
bodyTag := "bodyTag"
require.NoError(t, event.SaveBodyTag(bodyTag))
require.NoError(t, event.SaveDataItems("body_tag", bodyTag))
assert.Equal(t, bodyTag, event.GetBodyTag())
testMap := make(map[string]any, 5)
@@ -29,11 +29,11 @@ func TestBodyTag(t *testing.T) {
event.Data = data
assert.Equal(t, oldBodyTag, event.GetBodyTag())
require.NoError(t, event.SaveBodyTag(bodyTag))
require.NoError(t, event.SaveDataItems("body_tag", bodyTag))
assert.Equal(t, bodyTag, event.GetBodyTag())
// Make sure data was not modified
require.NoError(t, event.SaveBodyTag(oldBodyTag))
require.NoError(t, event.SaveDataItems("body_tag", oldBodyTag))
var result map[string]any
require.NoError(t, json.Unmarshal(event.Data, &result))
assert.Equal(t, testMap, result)
@@ -11728,11 +11728,15 @@ func (s *integrationEnterpriseTestSuite) TestCalendarCallback() {
require.NotZero(t, event.StartTime)
require.NotZero(t, event.EndTime)
require.NotEmpty(t, event.UUID)
bodyTag := event.GetBodyTag()
assert.NotEmpty(t, bodyTag)
assert.Equal(t, 1, calendar.MockChannelsCount())
// Get channel ID
type eventDetails struct {
ChannelID string `json:"channel_id"`
BodyTag string `json:"body_tag"`
ETag string `json:"etag"`
}
var details eventDetails
err = json.Unmarshal(event.Data, &details)
@@ -11855,6 +11859,8 @@ func (s *integrationEnterpriseTestSuite) TestCalendarCallback() {
err = json.Unmarshal(eventRecreated.Data, &details)
require.NoError(t, err)
assert.NotEmpty(t, details.BodyTag)
bodyTag = details.BodyTag
// New event callback should work
_ = s.DoRawWithHeaders("POST", "/api/v1/fleet/calendar/webhook/"+eventRecreated.UUID, []byte(""), http.StatusOK,
@@ -11891,6 +11897,30 @@ func (s *integrationEnterpriseTestSuite) TestCalendarCallback() {
assert.Greater(t, eventUpdated.StartTime, eventRecreated.StartTime)
assert.Equal(t, eventRecreated.EndTime, eventUpdated.EndTime)
assert.Equal(t, 1, calendar.MockChannelsCount())
assert.Equal(t, bodyTag, eventRecreated.GetBodyTag())
// Change the body contents of event.
events = calendar.ListGoogleMockEvents()
require.Len(t, events, 1)
eTag := "description change etag"
for _, e := range events {
e.Etag = eTag
e.Description = "new description"
}
// New event callback should cause Etag to update but Body tag to remain the same
_ = s.DoRawWithHeaders("POST", "/api/v1/fleet/calendar/webhook/"+eventRecreated.UUID, []byte(""), http.StatusOK,
map[string]string{
"X-Goog-Channel-Id": details.ChannelID,
"X-Goog-Resource-State": "exists",
})
team1CalendarEvents, err = s.ds.ListCalendarEvents(ctx, &team1.ID)
require.NoError(t, err)
require.Len(t, team1CalendarEvents, 1)
eventDescUpdated := team1CalendarEvents[0]
err = json.Unmarshal(eventDescUpdated.Data, &details)
require.NoError(t, err)
assert.Equal(t, bodyTag, details.BodyTag)
assert.Equal(t, eTag, details.ETag)
// Update the time of the event again
events = calendar.ListGoogleMockEvents()
@@ -11900,6 +11930,7 @@ func (s *integrationEnterpriseTestSuite) TestCalendarCallback() {
require.NoError(t, err)
newStartTime := st.Add(5 * time.Minute).Format(time.RFC3339)
e.Start.DateTime = newStartTime
e.Etag = e.Etag + "1"
}
// Grab the lock
@@ -11950,6 +11981,9 @@ func (s *integrationEnterpriseTestSuite) TestCalendarCallback() {
require.NoError(t, err)
if len(team1CalendarEvents) == 1 && team1CalendarEvents[0].UUID == event.UUID &&
team1CalendarEvents[0].StartTime.After(event.StartTime) {
err = json.Unmarshal(team1CalendarEvents[0].Data, &details)
require.NoError(t, err)
assert.NotEqual(t, eTag, details.ETag, "ETag should have updated")
done <- struct{}{}
return
}