Skip to content

Commit d7c8ddc

Browse files
Hook up Telemetry LSP event and add telemetry event when users opt-out/in to features (#1381)
* add telemetry sending * make it so telemetry event only happens when default is changed and add to a test * use var Co-authored-by: Robert Holt <[email protected]> Co-authored-by: Robert Holt <[email protected]>
1 parent 419fec4 commit d7c8ddc

File tree

7 files changed

+227
-79
lines changed

7 files changed

+227
-79
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
using System.Collections.Generic;
7+
using Newtonsoft.Json.Linq;
8+
9+
namespace Microsoft.PowerShell.EditorServices.Logging
10+
{
11+
// This inheirits from Dictionary so that it can be passed in to SendTelemetryEvent()
12+
// which takes in an IDictionary<string, JToken>
13+
// However, I wanted creation to be easy so you can do
14+
// new PsesTelemetryEvent { EventName = "eventName", Data = data }
15+
internal class PsesTelemetryEvent : Dictionary<string, JToken>
16+
{
17+
public string EventName
18+
{
19+
get
20+
{
21+
return this["EventName"].ToString() ?? "PsesEvent";
22+
}
23+
set
24+
{
25+
this["EventName"] = value;
26+
}
27+
}
28+
29+
public JObject Data
30+
{
31+
get
32+
{
33+
return this["Data"] as JObject ?? new JObject();
34+
}
35+
set
36+
{
37+
this["Data"] = value;
38+
}
39+
}
40+
}
41+
}

src/PowerShellEditorServices/Services/PowerShellContext/Handlers/GetCommandHandler.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
namespace Microsoft.PowerShell.EditorServices.Handlers
1616
{
17-
[Serial, Method("powerShell/getCommand")]
17+
[Serial, Method("powerShell/getCommand", Direction.ClientToServer)]
1818
internal interface IGetCommandHandler : IJsonRpcRequestHandler<GetCommandParams, List<PSCommandMessage>> { }
1919

2020
internal class GetCommandParams : IRequest<List<PSCommandMessage>> { }

src/PowerShellEditorServices/Services/PowerShellContext/Handlers/ITemplateHandlers.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88

99
namespace Microsoft.PowerShell.EditorServices.Handlers
1010
{
11-
[Serial, Method("powerShell/getProjectTemplates")]
11+
[Serial]
12+
[Method("powerShell/getProjectTemplates", Direction.ClientToServer)]
1213
internal interface IGetProjectTemplatesHandler : IJsonRpcRequestHandler<GetProjectTemplatesRequest, GetProjectTemplatesResponse> { }
1314

14-
[Serial, Method("powerShell/newProjectFromTemplate")]
15+
[Serial]
16+
[Method("powerShell/newProjectFromTemplate", Direction.ClientToServer)]
1517
internal interface INewProjectFromTemplateHandler : IJsonRpcRequestHandler<NewProjectFromTemplateRequest, NewProjectFromTemplateResponse> { }
1618

1719
internal class GetProjectTemplatesRequest : IRequest<GetProjectTemplatesResponse>

src/PowerShellEditorServices/Services/Workspace/Handlers/ConfigurationHandler.cs

+67-4
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,20 @@
55

66
using System;
77
using System.Collections.Generic;
8+
using System.IO;
89
using System.Threading;
910
using System.Threading.Tasks;
11+
using MediatR;
1012
using Microsoft.Extensions.Logging;
13+
using Microsoft.PowerShell.EditorServices.Logging;
1114
using Microsoft.PowerShell.EditorServices.Services;
1215
using Microsoft.PowerShell.EditorServices.Services.Configuration;
13-
using MediatR;
16+
using Newtonsoft.Json.Linq;
1417
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
1518
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
19+
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
20+
using OmniSharp.Extensions.LanguageServer.Protocol.Window;
1621
using OmniSharp.Extensions.LanguageServer.Protocol.Workspace;
17-
using System.IO;
1822

1923
namespace Microsoft.PowerShell.EditorServices.Handlers
2024
{
@@ -24,6 +28,7 @@ internal class PsesConfigurationHandler : IDidChangeConfigurationHandler
2428
private readonly WorkspaceService _workspaceService;
2529
private readonly ConfigurationService _configurationService;
2630
private readonly PowerShellContextService _powerShellContextService;
31+
private readonly ILanguageServerFacade _languageServer;
2732
private DidChangeConfigurationCapability _capability;
2833
private bool _profilesLoaded;
2934
private bool _consoleReplStarted;
@@ -34,13 +39,14 @@ public PsesConfigurationHandler(
3439
WorkspaceService workspaceService,
3540
AnalysisService analysisService,
3641
ConfigurationService configurationService,
37-
PowerShellContextService powerShellContextService)
42+
PowerShellContextService powerShellContextService,
43+
ILanguageServerFacade languageServer)
3844
{
3945
_logger = factory.CreateLogger<PsesConfigurationHandler>();
4046
_workspaceService = workspaceService;
4147
_configurationService = configurationService;
4248
_powerShellContextService = powerShellContextService;
43-
49+
_languageServer = languageServer;
4450
ConfigurationUpdated += analysisService.OnConfigurationUpdated;
4551
}
4652

@@ -57,6 +63,8 @@ public async Task<Unit> Handle(DidChangeConfigurationParams request, Cancellatio
5763
return await Unit.Task.ConfigureAwait(false);
5864
}
5965

66+
SendFeatureChangesTelemetry(incomingSettings);
67+
6068
bool oldLoadProfiles = _configurationService.CurrentSettings.EnableProfileLoading;
6169
bool oldScriptAnalysisEnabled =
6270
_configurationService.CurrentSettings.ScriptAnalysis.Enable ?? false;
@@ -141,6 +149,61 @@ await _powerShellContextService.SetWorkingDirectoryAsync(
141149
return await Unit.Task.ConfigureAwait(false);
142150
}
143151

152+
private void SendFeatureChangesTelemetry(LanguageServerSettingsWrapper incomingSettings)
153+
{
154+
var configChanges = new Dictionary<string, bool>();
155+
// Send telemetry if the user opted-out of ScriptAnalysis
156+
if (incomingSettings.Powershell.ScriptAnalysis.Enable == false &&
157+
_configurationService.CurrentSettings.ScriptAnalysis.Enable != incomingSettings.Powershell.ScriptAnalysis.Enable)
158+
{
159+
configChanges["ScriptAnalysis"] = incomingSettings.Powershell.ScriptAnalysis.Enable ?? false;
160+
}
161+
162+
// Send telemetry if the user opted-out of CodeFolding
163+
if (!incomingSettings.Powershell.CodeFolding.Enable &&
164+
_configurationService.CurrentSettings.CodeFolding.Enable != incomingSettings.Powershell.CodeFolding.Enable)
165+
{
166+
configChanges["CodeFolding"] = incomingSettings.Powershell.CodeFolding.Enable;
167+
}
168+
169+
// Send telemetry if the user opted-out of the prompt to update PackageManagement
170+
if (!incomingSettings.Powershell.PromptToUpdatePackageManagement &&
171+
_configurationService.CurrentSettings.PromptToUpdatePackageManagement != incomingSettings.Powershell.PromptToUpdatePackageManagement)
172+
{
173+
configChanges["PromptToUpdatePackageManagement"] = incomingSettings.Powershell.PromptToUpdatePackageManagement;
174+
}
175+
176+
// Send telemetry if the user opted-out of Profile loading
177+
if (!incomingSettings.Powershell.EnableProfileLoading &&
178+
_configurationService.CurrentSettings.EnableProfileLoading != incomingSettings.Powershell.EnableProfileLoading)
179+
{
180+
configChanges["ProfileLoading"] = incomingSettings.Powershell.EnableProfileLoading;
181+
}
182+
183+
// Send telemetry if the user opted-in to Pester 5+ CodeLens
184+
if (!incomingSettings.Powershell.Pester.UseLegacyCodeLens &&
185+
_configurationService.CurrentSettings.Pester.UseLegacyCodeLens != incomingSettings.Powershell.Pester.UseLegacyCodeLens)
186+
{
187+
// From our perspective we want to see how many people are opting in to this so we flip the value
188+
configChanges["Pester5CodeLens"] = !incomingSettings.Powershell.Pester.UseLegacyCodeLens;
189+
}
190+
191+
// No need to send any telemetry since nothing changed
192+
if (configChanges.Count == 0)
193+
{
194+
return;
195+
}
196+
197+
_languageServer.Window.SendTelemetryEvent(new TelemetryEventParams
198+
{
199+
Data = new PsesTelemetryEvent
200+
{
201+
EventName = "NonDefaultPsesFeatureConfiguration",
202+
Data = JObject.FromObject(configChanges)
203+
}
204+
});
205+
}
206+
144207
public void SetCapability(DidChangeConfigurationCapability capability)
145208
{
146209
_capability = capability;

src/PowerShellEditorServices/Services/Workspace/LanguageServerSettings.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ namespace Microsoft.PowerShell.EditorServices.Services.Configuration
1818
internal class LanguageServerSettings
1919
{
2020
private readonly object updateLock = new object();
21-
public bool EnableProfileLoading { get; set; }
2221

23-
public bool PromptToUpdatePackageManagement { get; set; }
22+
public bool EnableProfileLoading { get; set; } = true;
23+
24+
public bool PromptToUpdatePackageManagement { get; set; } = true;
2425

2526
public ScriptAnalysisSettings ScriptAnalysis { get; set; }
2627

test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs

+18-11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
using System.Threading;
1111
using System.Threading.Tasks;
1212
using Microsoft.Extensions.Logging;
13+
using Microsoft.PowerShell.EditorServices.Logging;
14+
using Microsoft.PowerShell.EditorServices.Services.Configuration;
1315
using Newtonsoft.Json.Linq;
1416
using OmniSharp.Extensions.LanguageServer.Client;
1517
using OmniSharp.Extensions.LanguageServer.Protocol;
@@ -29,7 +31,7 @@ public class LSPTestsFixture : TestsFixture
2931

3032
public ILanguageClient PsesLanguageClient { get; private set; }
3133
public List<Diagnostic> Diagnostics { get; set; }
32-
34+
internal List<PsesTelemetryEvent> TelemetryEvents { get; set; }
3335
public ITestOutputHelper Output { get; set; }
3436

3537
public async override Task CustomInitializeAsync(
@@ -38,6 +40,7 @@ public async override Task CustomInitializeAsync(
3840
Stream outputStream)
3941
{
4042
Diagnostics = new List<Diagnostic>();
43+
TelemetryEvents = new List<PsesTelemetryEvent>();
4144
DirectoryInfo testdir =
4245
Directory.CreateDirectory(Path.Combine(s_binDir, Path.GetRandomFileName()));
4346

@@ -48,7 +51,13 @@ public async override Task CustomInitializeAsync(
4851
.WithOutput(outputStream)
4952
.WithRootUri(DocumentUri.FromFileSystemPath(testdir.FullName))
5053
.OnPublishDiagnostics(diagnosticParams => Diagnostics.AddRange(diagnosticParams.Diagnostics.Where(d => d != null)))
51-
.OnLogMessage(logMessageParams => Output?.WriteLine($"{logMessageParams.Type.ToString()}: {logMessageParams.Message}"));
54+
.OnLogMessage(logMessageParams => Output?.WriteLine($"{logMessageParams.Type.ToString()}: {logMessageParams.Message}"))
55+
.OnTelemetryEvent(telemetryEventParams => TelemetryEvents.Add(
56+
new PsesTelemetryEvent
57+
{
58+
EventName = (string) telemetryEventParams.Data["eventName"],
59+
Data = telemetryEventParams.Data["data"] as JObject
60+
}));
5261

5362
// Enable all capabilities this this is for testing.
5463
// This will be a built in feature of the Omnisharp client at some point.
@@ -63,18 +72,16 @@ public async override Task CustomInitializeAsync(
6372
await PsesLanguageClient.Initialize(CancellationToken.None).ConfigureAwait(false);
6473

6574
// Make sure Script Analysis is enabled because we'll need it in the tests.
75+
// This also makes sure the configuration is set to default values.
6676
PsesLanguageClient.Workspace.DidChangeConfiguration(
6777
new DidChangeConfigurationParams
6878
{
69-
Settings = JObject.Parse(@"
70-
{
71-
""powershell"": {
72-
""scriptAnalysis"": {
73-
""enable"": true
74-
}
75-
}
76-
}
77-
")
79+
Settings = JToken.FromObject(new LanguageServerSettingsWrapper
80+
{
81+
Files = new EditorFileSettings(),
82+
Search = new EditorSearchSettings(),
83+
Powershell = new LanguageServerSettings()
84+
})
7885
});
7986
}
8087

0 commit comments

Comments
 (0)