Skip to content

Commit 7933099

Browse files
authored
Add more telemetry to the role editor (#54486)
Whenever the user clicks the save button, report whether the standard and YAML modes were used and whether there were any YAML fields that prevented the user from accessing the standard editor.
1 parent 07bba43 commit 7933099

File tree

16 files changed

+923
-445
lines changed

16 files changed

+923
-445
lines changed

api/gen/proto/go/usageevents/v1/usageevents.pb.go

Lines changed: 560 additions & 365 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/proto/teleport/usageevents/v1/usageevents.proto

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,12 @@ message UIDiscoverCompletedEvent {
409409
message UICreateNewRoleClickEvent {}
410410

411411
// UICreateNewRoleSaveClickEvent is an event that can be triggered during custom role creation
412-
message UICreateNewRoleSaveClickEvent {}
412+
message UICreateNewRoleSaveClickEvent {
413+
bool standard_used = 1;
414+
bool yaml_used = 2;
415+
string mode_when_saved = 3;
416+
repeated string fields_with_conversion_errors = 4;
417+
}
413418

414419
// UICreateNewRoleCancelClickEvent is an event that can be triggered during custom role creation
415420
message UICreateNewRoleCancelClickEvent {}

gen/proto/go/prehog/v1alpha/teleport.pb.go

Lines changed: 52 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/proto/ts/prehog/v1alpha/teleport_pb.ts

Lines changed: 64 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/usagereporter/teleport/types.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,11 @@ func (u *UICreateNewRoleSaveClickEvent) Anonymize(a utils.Anonymizer) prehogv1a.
504504
return prehogv1a.SubmitEventRequest{
505505
Event: &prehogv1a.SubmitEventRequest_UiCreateNewRoleSaveClick{
506506
UiCreateNewRoleSaveClick: &prehogv1a.UICreateNewRoleSaveClickEvent{
507-
UserName: a.AnonymizeString(u.UserName),
507+
UserName: a.AnonymizeString(u.UserName),
508+
StandardUsed: u.StandardUsed,
509+
YamlUsed: u.YamlUsed,
510+
ModeWhenSaved: u.ModeWhenSaved,
511+
FieldsWithConversionErrors: u.FieldsWithConversionErrors,
508512
},
509513
},
510514
}
@@ -1426,7 +1430,11 @@ func ConvertUsageEvent(event *usageeventsv1.UsageEventOneOf, userMD UserMetadata
14261430
}, nil
14271431
case *usageeventsv1.UsageEventOneOf_UiCreateNewRoleSaveClick:
14281432
return &UICreateNewRoleSaveClickEvent{
1429-
UserName: userMD.Username,
1433+
UserName: userMD.Username,
1434+
StandardUsed: e.UiCreateNewRoleSaveClick.StandardUsed,
1435+
YamlUsed: e.UiCreateNewRoleSaveClick.YamlUsed,
1436+
ModeWhenSaved: e.UiCreateNewRoleSaveClick.ModeWhenSaved,
1437+
FieldsWithConversionErrors: e.UiCreateNewRoleSaveClick.FieldsWithConversionErrors,
14301438
}, nil
14311439
case *usageeventsv1.UsageEventOneOf_UiCreateNewRoleCancelClick:
14321440
return &UICreateNewRoleCancelClickEvent{

lib/usagereporter/web/userevent.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,22 @@ func ConvertUserEventRequestToUsageEvent(req CreateUserEventRequest) (*usageeven
354354
nil
355355

356356
case createNewRoleSaveClickEvent:
357+
roleEvent := struct {
358+
StandardUsed bool `json:"standardUsed"`
359+
YAMLUsed bool `json:"yamlUsed"`
360+
ModeWhenSaved string `json:"modeWhenSaved"`
361+
FieldsWithConversionErrors []string `json:"fieldsWithConversionErrors"`
362+
}{}
363+
if err := json.Unmarshal([]byte(*req.EventData), &roleEvent); err != nil {
364+
return nil, trace.BadParameter("eventData is invalid: %v", err)
365+
}
357366
return &usageeventsv1.UsageEventOneOf{Event: &usageeventsv1.UsageEventOneOf_UiCreateNewRoleSaveClick{
358-
UiCreateNewRoleSaveClick: &usageeventsv1.UICreateNewRoleSaveClickEvent{},
367+
UiCreateNewRoleSaveClick: &usageeventsv1.UICreateNewRoleSaveClickEvent{
368+
StandardUsed: roleEvent.StandardUsed,
369+
YamlUsed: roleEvent.YAMLUsed,
370+
ModeWhenSaved: roleEvent.ModeWhenSaved,
371+
FieldsWithConversionErrors: roleEvent.FieldsWithConversionErrors,
372+
},
359373
}},
360374
nil
361375

proto/prehog/v1alpha/teleport.proto

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,21 @@ message UICreateNewRoleClickEvent {
801801
message UICreateNewRoleSaveClickEvent {
802802
//anonymized
803803
string user_name = 1;
804+
// StandardUsed indicates if the user interacted with the standard editor
805+
// tab.
806+
bool standard_used = 2;
807+
// YamlUsed indicates if the user interacted with the YAML editor tab.
808+
bool yaml_used = 3;
809+
// ModeWhenSaved indicates which editor tab was active when the Save button
810+
// was clicked ("standard" or "yaml"). Note that it's inherently different
811+
// from what `StandardUsed` or `YamlUsed` describe; the user can interact
812+
// with one kind of editor, then verify the results in another, and
813+
// ultimately click Save on any of them.
814+
string mode_when_saved = 4;
815+
// A list of field paths that prevented the standard editor from being
816+
// operational, if any. Looking at these may provide insight into which
817+
// missing features should be implemented first.
818+
repeated string fields_with_conversion_errors = 5;
804819
}
805820

806821
// UICreateNewRoleCancelClickEvent is an event that can be triggered during custom role creation

web/packages/teleport/src/Roles/RoleEditor/RoleEditor.test.tsx

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ import ResourceService, {
2929
RoleWithYaml,
3030
} from 'teleport/services/resources';
3131
import { storageService } from 'teleport/services/storageService';
32-
import { CaptureEvent, userEventService } from 'teleport/services/userEvent';
32+
import {
33+
CaptureEvent,
34+
CreateNewRoleSaveClickEventData,
35+
RoleEditorMode,
36+
userEventService,
37+
} from 'teleport/services/userEvent';
3338
import { yamlService } from 'teleport/services/yaml';
3439
import {
3540
YamlStringifyRequest,
@@ -78,6 +83,9 @@ beforeEach(() => {
7883
return toFauxYaml(withDefaults(req.resource));
7984
});
8085
jest.spyOn(userEventService, 'captureUserEvent').mockImplementation(() => {});
86+
jest
87+
.spyOn(userEventService, 'captureCreateNewRoleSaveClickEvent')
88+
.mockImplementation(() => {});
8189
jest
8290
.spyOn(ResourceService.prototype, 'fetchRole')
8391
.mockImplementation(async name => {
@@ -133,6 +141,7 @@ test('rendering and switching tabs for new role', async () => {
133141
}, 10000);
134142

135143
test('rendering and switching tabs for a non-standard role', async () => {
144+
const onSave = jest.fn();
136145
const originalRole = withDefaults({
137146
metadata: {
138147
name: 'some-role',
@@ -142,7 +151,12 @@ test('rendering and switching tabs for a non-standard role', async () => {
142151
deny: { node_labels: { foo: ['bar'] } },
143152
},
144153
});
145-
render(<TestRoleEditor originalRole={newRoleWithYaml(originalRole)} />);
154+
render(
155+
<TestRoleEditor
156+
originalRole={newRoleWithYaml(originalRole)}
157+
onSave={onSave}
158+
/>
159+
);
146160
expect(getYamlEditorTab()).toHaveAttribute('aria-selected', 'true');
147161
expect(fromFauxYaml(await getTextEditorContents())).toEqual(originalRole);
148162
expect(screen.getByRole('button', { name: 'Save Changes' })).toBeDisabled();
@@ -156,6 +170,22 @@ test('rendering and switching tabs for a non-standard role', async () => {
156170
await user.click(getYamlEditorTab());
157171
expect(fromFauxYaml(await getTextEditorContents())).toEqual(originalRole);
158172
expect(screen.getByRole('button', { name: 'Save Changes' })).toBeDisabled();
173+
174+
await user.clear(await findTextEditor());
175+
await user.type(await findTextEditor(), '{{"asdf":"qwer"}');
176+
await user.click(screen.getByRole('button', { name: 'Save Changes' }));
177+
178+
expect(onSave).toHaveBeenCalledWith({
179+
yaml: '{"asdf":"qwer"}',
180+
});
181+
expect(
182+
userEventService.captureCreateNewRoleSaveClickEvent
183+
).toHaveBeenCalledWith({
184+
standardUsed: false,
185+
yamlUsed: true,
186+
modeWhenSaved: RoleEditorMode.Yaml,
187+
fieldsWithConversionErrors: ['spec.deny.node_labels'],
188+
} as CreateNewRoleSaveClickEventData);
159189
});
160190

161191
it('calls onRoleUpdate on each modification in the standard editor', async () => {
@@ -355,9 +385,14 @@ test('saving a new role', async () => {
355385
version: 'v8',
356386
},
357387
});
358-
expect(userEventService.captureUserEvent).toHaveBeenCalledWith({
359-
event: CaptureEvent.CreateNewRoleSaveClickEvent,
360-
});
388+
expect(
389+
userEventService.captureCreateNewRoleSaveClickEvent
390+
).toHaveBeenCalledWith({
391+
standardUsed: true,
392+
yamlUsed: false,
393+
modeWhenSaved: RoleEditorMode.Standard,
394+
fieldsWithConversionErrors: [],
395+
} as CreateNewRoleSaveClickEventData);
361396
});
362397

363398
describe('saving a new role after editing as YAML', () => {
@@ -373,9 +408,14 @@ describe('saving a new role after editing as YAML', () => {
373408
expect(onSave).toHaveBeenCalledWith({
374409
yaml: '{"foo":"bar"}',
375410
});
376-
expect(userEventService.captureUserEvent).toHaveBeenCalledWith({
377-
event: CaptureEvent.CreateNewRoleSaveClickEvent,
378-
});
411+
expect(
412+
userEventService.captureCreateNewRoleSaveClickEvent
413+
).toHaveBeenCalledWith({
414+
standardUsed: false,
415+
yamlUsed: true,
416+
modeWhenSaved: RoleEditorMode.Yaml,
417+
fieldsWithConversionErrors: [],
418+
} as CreateNewRoleSaveClickEventData);
379419
});
380420

381421
test('with Policy enabled', async () => {
@@ -407,9 +447,14 @@ describe('saving a new role after editing as YAML', () => {
407447
expect(onSave).toHaveBeenCalledWith({
408448
yaml: '{"metadata":{"description":"foo"}}',
409449
});
410-
expect(userEventService.captureUserEvent).toHaveBeenCalledWith({
411-
event: CaptureEvent.CreateNewRoleSaveClickEvent,
412-
});
450+
expect(
451+
userEventService.captureCreateNewRoleSaveClickEvent
452+
).toHaveBeenCalledWith({
453+
standardUsed: false,
454+
yamlUsed: true,
455+
modeWhenSaved: RoleEditorMode.Yaml,
456+
fieldsWithConversionErrors: [],
457+
} as CreateNewRoleSaveClickEventData);
413458
});
414459
});
415460

0 commit comments

Comments
 (0)