Add hostname to calendar event (maintenance window) bodies (#20235)

## Addresses #19281 

<img width="825" alt="Screenshot 2024-07-05 at 10 47 25 AM"
src="https://github.com/fleetdm/fleet/assets/61553566/d50ad460-b358-48aa-8e93-2f6d09472d87">



- [x] Changes file added for user-visible changes in `changes/`,
- [x] Added/updated tests
- [x] Manual QA for all new/changed functionality

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
jacobshandling 2024-07-08 14:25:49 -07:00 committed by GitHub
parent 28ca463d13
commit 20e1e4edfe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 41 additions and 24 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 137 KiB

View file

@ -0,0 +1 @@
* Add host's display name to calendar event descriptions

View file

@ -63,7 +63,7 @@ const CalendarEventPreviewModal = ({
</div>
<div className={`${baseClass}__preview-info__text`}>
{orgName} reserved this time to make some changes to your work
computer.
computer (Anna&apos;s MacBook Pro).
<br />
<br />
Please leave your device on and connected to power.

View file

@ -242,8 +242,8 @@ const CalendarEventsModal = ({
you must first connect Fleet to your Google Workspace service account.
</div>
<div>
This can be configured in{" "}
<b>Settings &gt; Integrations &gt; Calendars.</b>
This can be configured in <b>Settings</b> &gt; <b>Integrations</b>{" "}
&gt; <b>Calendars.</b>
</div>
<CustomLink
url="https://www.fleetdm.com/learn-more-about/calendar-events"

View file

@ -706,8 +706,8 @@ func TestCalendarEvents1KHosts(t *testing.T) {
require.Len(t, createdCalendarEvents, 0)
}
// TestEventDescription tests generation of the event description.
func TestEventDescription(t *testing.T) {
// TestEventBody tests generation of the event body.
func TestEventBody(t *testing.T) {
ds := new(mock.Store)
ctx := context.Background()
logger := kitlog.With(kitlog.NewLogfmtLogger(os.Stdout))
@ -805,13 +805,13 @@ func TestEventDescription(t *testing.T) {
}, nil
}
hostID1, userEmail1 := uint(100), "user1@example.com"
hostID2, userEmail2 := uint(101), "user2@example.com"
hostID3, userEmail3 := uint(102), "user3@example.com"
hostID4, userEmail4 := uint(103), "user4@example.com"
hostID5, userEmail5 := uint(104), "user5@example.com"
hostID6, userEmail6 := uint(105), "user6@example.com"
hostID7, userEmail7 := uint(106), "user7@example.com"
hostID1, userEmail1, hostDisplayName1 := uint(100), "user1@example.com", "Host 1"
hostID2, userEmail2, hostDisplayName2 := uint(101), "user2@example.com", "Host 2"
hostID3, userEmail3, hostDisplayName3 := uint(102), "user3@example.com", "Host 3"
hostID4, userEmail4, hostDisplayName4 := uint(103), "user4@example.com", "Host 4"
hostID5, userEmail5, hostDisplayName5 := uint(104), "user5@example.com", "Host 5"
hostID6, userEmail6, hostDisplayName6 := uint(105), "user6@example.com", "Host 6"
hostID7, userEmail7, hostDisplayName7 := uint(106), "user7@example.com", "Host 7"
ds.GetTeamHostsPolicyMembershipsFunc = func(
ctx context.Context, domain string, teamID uint, policyIDs []uint, _ *uint,
@ -823,42 +823,49 @@ func TestEventDescription(t *testing.T) {
{
HostID: hostID1,
Email: userEmail1,
HostDisplayName: hostDisplayName1,
Passing: false,
FailingPolicyIDs: fmt.Sprintf("%d", policyID1),
},
{
HostID: hostID2,
Email: userEmail2,
HostDisplayName: hostDisplayName2,
Passing: false,
FailingPolicyIDs: fmt.Sprintf("%d", policyID2),
},
{
HostID: hostID3,
Email: userEmail3,
HostDisplayName: hostDisplayName3,
Passing: false,
FailingPolicyIDs: fmt.Sprintf("%d", policyID3),
},
{
HostID: hostID4,
Email: userEmail4,
HostDisplayName: hostDisplayName4,
Passing: false,
FailingPolicyIDs: fmt.Sprintf("%d", policyID4),
},
{
HostID: hostID5,
Email: userEmail5,
HostDisplayName: hostDisplayName5,
Passing: false,
FailingPolicyIDs: fmt.Sprintf("%d,%d,%d,%d", policyID1, policyID2, policyID3, policyID4),
},
{
HostID: hostID6,
Email: userEmail6,
HostDisplayName: hostDisplayName6,
Passing: false,
FailingPolicyIDs: fmt.Sprintf("%d", policyID1),
},
{
HostID: hostID7,
Email: userEmail7,
HostDisplayName: hostDisplayName7,
Passing: false,
FailingPolicyIDs: fmt.Sprintf("%d", policyID5),
},
@ -956,15 +963,21 @@ func TestEventDescription(t *testing.T) {
var details map[string]string
err = json.Unmarshal(calendarEvents[hostCalEvent.HostID].Data, &details)
require.NoError(t, err)
description := createdCalendarEvents[details["id"]].Description
defaultDescriptionWithOrg := fmt.Sprintf("%s %s", orgName, fleet.CalendarDefaultDescription)
// What Google Calendar calls the "Description" is what Fleet calls the "Body," since the Body
// contains a description and a resolution.
eventBody := createdCalendarEvents[details["id"]].Description
switch hostCalEvent.HostID {
case hostID1, hostID6:
assert.Contains(t, description, "Description for policy 1")
assert.Contains(t, description, "Resolution for policy 1")
case hostID1:
assert.Contains(t, eventBody, fmt.Sprintf(`%s %s (Host 1).`, orgName, fleet.CalendarBodyStaticHeader))
assert.Contains(t, eventBody, "Description for policy 1")
assert.Contains(t, eventBody, "Resolution for policy 1")
case hostID6:
assert.Contains(t, eventBody, fmt.Sprintf(`%s %s (Host 6).`, orgName, fleet.CalendarBodyStaticHeader))
assert.Contains(t, eventBody, "Description for policy 1")
assert.Contains(t, eventBody, "Resolution for policy 1")
default:
assert.Contains(t, description, defaultDescriptionWithOrg)
assert.Contains(t, description, fleet.CalendarDefaultResolution)
assert.Contains(t, eventBody, fmt.Sprintf(`%s %s (Host`, orgName, fleet.CalendarBodyStaticHeader))
assert.Contains(t, eventBody, fleet.CalendarDefaultResolution)
}
}
}

View file

@ -10,6 +10,7 @@ import (
)
const (
CalendarBodyStaticHeader = "reserved this time to make some changes to your work computer"
CalendarDefaultDescription = "needs to make sure your device meets the organization's requirements."
CalendarDefaultResolution = "During this maintenance window, you can expect updates to be applied automatically. Your device may be unavailable during this time."
)

View file

@ -33,14 +33,15 @@ func CreateUserCalendarFromConfig(ctx context.Context, config *CalendarConfig, l
}
func GenerateCalendarEventBody(ctx context.Context, ds fleet.Datastore, orgName string, host fleet.HostPolicyMembershipData,
policyIDtoPolicy *sync.Map, conflict bool, logger kitlog.Logger) string {
policyIDtoPolicy *sync.Map, conflict bool, logger kitlog.Logger,
) string {
description, resolution := getCalendarEventDescriptionAndResolution(ctx, ds, orgName, host, policyIDtoPolicy, logger)
conflictStr := ""
if conflict {
conflictStr = " because there was no remaining availability"
conflictStr = "because there was no remaining availability "
}
return fmt.Sprintf(`%s reserved this time to make some changes to your work computer%s.
return fmt.Sprintf(`%s %s %s(%s).
Please leave your device on and connected to power.
@ -49,11 +50,12 @@ Please leave your device on and connected to power.
<b>What we'll do</b>
%s
`, orgName, conflictStr, description, resolution)
`, orgName, fleet.CalendarBodyStaticHeader, conflictStr, host.HostDisplayName, description, resolution)
}
func getCalendarEventDescriptionAndResolution(ctx context.Context, ds fleet.Datastore, orgName string, host fleet.HostPolicyMembershipData,
policyIDtoPolicy *sync.Map, logger kitlog.Logger) (string, string) {
policyIDtoPolicy *sync.Map, logger kitlog.Logger,
) (string, string) {
getDefaultDescription := func() string {
return fmt.Sprintf(`%s %s`, orgName, fleet.CalendarDefaultDescription)
}