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:
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user