diff --git a/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs b/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs index 5bbbdb53a31..f7ba0fda56d 100644 --- a/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs +++ b/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs @@ -7,8 +7,6 @@ using Bicep.Core; using Bicep.Core.Extensions; using Bicep.Core.FileSystem; -using Bicep.Core.Navigation; -using Bicep.Core.Parsing; using Bicep.Core.Semantics; using Bicep.Core.Semantics.Namespaces; using Bicep.Core.Syntax; @@ -20,7 +18,9 @@ using Bicep.LanguageServer.Telemetry; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; using SymbolKind = Bicep.Core.Semantics.SymbolKind; namespace Bicep.LangServer.UnitTests @@ -28,6 +28,9 @@ namespace Bicep.LangServer.UnitTests [TestClass] public class BicepCompletionProviderTests { + private static readonly MockRepository Repository = new MockRepository(MockBehavior.Strict); + private static readonly ILanguageServerFacade Server = Repository.Create().Object; + [TestMethod] public void DeclarationContextShouldReturnKeywordCompletions() { @@ -35,7 +38,7 @@ public void DeclarationContextShouldReturnKeywordCompletions() var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), grouping); compilation.GetEntrypointSemanticModel().GetAllDiagnostics().Should().BeEmpty(); - var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider(Server)); var completions = provider.GetFilteredCompletions(compilation, BicepCompletionContext.Create(compilation, 0)); @@ -115,7 +118,7 @@ param p string var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Value.Span.Position; var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), grouping); - var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider(Server)); var context = BicepCompletionContext.Create(compilation, offset); var completions = provider.GetFilteredCompletions(compilation, context).ToList(); @@ -154,7 +157,7 @@ public void CompletionsForOneLinerParameterDefaultValueShouldIncludeFunctionsVal var offset = ((ParameterDefaultValueSyntax) grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Modifier!).DefaultValue.Span.Position; - var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider(Server)); var completions = provider.GetFilteredCompletions( compilation, BicepCompletionContext.Create(compilation, offset)).ToList(); @@ -185,7 +188,7 @@ public void CompletionsForModifierDefaultValuesShouldIncludeFunctionsValidInDefa var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), grouping); var context = BicepCompletionContext.Create(compilation, offset); - var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider(Server)); var completions = provider.GetFilteredCompletions(compilation, context).ToList(); AssertExpectedFunctions(completions, expectParamDefaultFunctions: true); @@ -216,7 +219,7 @@ param concat string var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Value.Span.Position; var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), grouping); - var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider(Server)); var context = BicepCompletionContext.Create(compilation, offset); var completions = provider.GetFilteredCompletions(compilation, context).ToList(); @@ -258,7 +261,7 @@ public void OutputTypeContextShouldReturnDeclarationTypeCompletions() { var grouping = SyntaxTreeGroupingFactory.CreateFromText("output test "); var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), grouping); - var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider(Server)); var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Type.Span.Position; @@ -275,7 +278,7 @@ public void ParameterTypeContextShouldReturnDeclarationTypeCompletions() { var grouping = SyntaxTreeGroupingFactory.CreateFromText("param foo "); var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), grouping); - var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider(Server)); var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Type.Span.Position; @@ -322,7 +325,7 @@ public void VerifyParameterTypeCompletionWithPrecedingComment() { var grouping = SyntaxTreeGroupingFactory.CreateFromText("/*test*/param foo "); var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), grouping); - var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider(Server)); var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Type.Span.Position; @@ -380,7 +383,7 @@ public void CommentShouldNotGiveAnyCompletions(string codeFragment) { var grouping = SyntaxTreeGroupingFactory.CreateFromText(codeFragment); var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), grouping); - var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider(Server)); var offset = codeFragment.IndexOf('|'); diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index 5398634fa19..b6070de75f4 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -85,7 +85,7 @@ private IEnumerable GetDeclarationCompletions(BicepCompletionCon foreach (Snippet resourceSnippet in SnippetsProvider.GetTopLevelNamedDeclarationSnippets()) { - TelemetryEvent telemetryEvent = TelemetryEvent.Create(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); + BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.Create(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); telemetryEvent.Set("name", resourceSnippet.Prefix); Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); diff --git a/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs index 45344f430cd..78c384960b9 100644 --- a/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs @@ -1,43 +1,28 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Linq; using System.Threading; using System.Threading.Tasks; using Bicep.LanguageServer.Telemetry; using MediatR; -using Newtonsoft.Json.Linq; -using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Workspace; namespace Bicep.LanguageServer.Handlers { - public class BicepTelemetryHandler : ExecuteCommandHandler + public class BicepTelemetryHandler : ExecuteCommandHandlerBase { private readonly ITelemetryProvider TelemetryProvider; - public BicepTelemetryHandler(ITelemetryProvider telemetryProvider) - : base(GetExecuteCommandRegistrationOptions()) + public BicepTelemetryHandler(ITelemetryProvider telemetryProvider, ISerializer serializer) + : base(TelemetryConstants.CommandName, serializer) { TelemetryProvider = telemetryProvider; } - private static ExecuteCommandRegistrationOptions GetExecuteCommandRegistrationOptions() - => new ExecuteCommandRegistrationOptions() - { - Commands = new Container(TelemetryConstants.CommandName) - }; - - public override Task Handle(ExecuteCommandParams request, CancellationToken cancellationToken) + public override Task Handle(BicepTelemetryEvent bicepTelemetryEvent, CancellationToken cancellationToken) { - JArray? arguments = request.Arguments; - if (arguments is not null && arguments.Any() && - arguments[0] is JToken jToken && - jToken.ToObject() is TelemetryEvent telemetryEvent) - { - TelemetryProvider.PostEvent(telemetryEvent); - } - + TelemetryProvider.PostEvent(bicepTelemetryEvent); return Unit.Task; } } diff --git a/src/Bicep.LangServer/Program.cs b/src/Bicep.LangServer/Program.cs index 9ef10d041f1..e4b2ecf5c53 100644 --- a/src/Bicep.LangServer/Program.cs +++ b/src/Bicep.LangServer/Program.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Bicep.Core.FileSystem; using Bicep.Core.TypeSystem.Az; -using Bicep.LanguageServer.Telemetry; namespace Bicep.LanguageServer { @@ -22,8 +21,7 @@ public static async Task Main(string[] args) new Server.CreationOptions { ResourceTypeProvider = AzResourceTypeProvider.CreateWithAzTypes(), - FileResolver = new FileResolver(), - TelemetryProvider = new TelemetryProvider() + FileResolver = new FileResolver() }); await server.RunAsync(cancellationToken); diff --git a/src/Bicep.LangServer/Server.cs b/src/Bicep.LangServer/Server.cs index 8cfcb11dc71..f4d7ad946c4 100644 --- a/src/Bicep.LangServer/Server.cs +++ b/src/Bicep.LangServer/Server.cs @@ -31,8 +31,6 @@ public class CreationOptions public IResourceTypeProvider? ResourceTypeProvider { get; set; } public IFileResolver? FileResolver { get; set; } - - public ITelemetryProvider? TelemetryProvider { get; set; } } private readonly OmnisharpLanguageServer server; @@ -74,11 +72,6 @@ private Server(CreationOptions creationOptions, Action on onOptionsFunc(options); }); - - if (creationOptions.TelemetryProvider is TelemetryProvider telemetryProvider) - { - telemetryProvider.LanguageServer = server; - } } public async Task RunAsync(CancellationToken cancellationToken) @@ -95,7 +88,7 @@ private static void RegisterServices(CreationOptions creationOptions, IServiceCo services.AddSingleton(services => creationOptions.ResourceTypeProvider ?? AzResourceTypeProvider.CreateWithAzTypes()); services.AddSingleton(services => creationOptions.SnippetsProvider ?? new SnippetsProvider()); services.AddSingleton(services => creationOptions.FileResolver ?? new FileResolver()); - services.AddSingleton(services => creationOptions.TelemetryProvider ?? new TelemetryProvider()); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs b/src/Bicep.LangServer/Telemetry/BicepTelemetryEvent.cs similarity index 60% rename from src/Bicep.LangServer/Telemetry/TelemetryEvent.cs rename to src/Bicep.LangServer/Telemetry/BicepTelemetryEvent.cs index 19ce088f650..090466173e5 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs +++ b/src/Bicep.LangServer/Telemetry/BicepTelemetryEvent.cs @@ -6,15 +6,15 @@ namespace Bicep.LanguageServer.Telemetry { - public class TelemetryEvent : TelemetryEventParams + public class BicepTelemetryEvent : TelemetryEventParams { - public Dictionary Properties = new Dictionary(); + public Dictionary Properties { get; set; } = new Dictionary(); public string? EventName { get; set; } - public static TelemetryEvent Create(string eventName) + public static BicepTelemetryEvent Create(string eventName) { - TelemetryEvent telemetryEvent = new TelemetryEvent(); + BicepTelemetryEvent telemetryEvent = new BicepTelemetryEvent(); telemetryEvent.EventName = eventName; return telemetryEvent; } @@ -22,7 +22,7 @@ public static TelemetryEvent Create(string eventName) public static class TelemetryExtensions { - public static void Set(this TelemetryEvent telemetryEvent, string name, string value) + public static void Set(this BicepTelemetryEvent telemetryEvent, string name, string value) { if (!telemetryEvent.Properties.ContainsKey(name)) { diff --git a/src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs b/src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs index 59e7dd1da02..0cdca0407ee 100644 --- a/src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs +++ b/src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs @@ -5,6 +5,6 @@ namespace Bicep.LanguageServer.Telemetry { public interface ITelemetryProvider { - void PostEvent(TelemetryEvent telemetryEvent); + void PostEvent(BicepTelemetryEvent telemetryEvent); } } diff --git a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs index b622494732c..c793580956d 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs +++ b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs @@ -8,18 +8,23 @@ namespace Bicep.LanguageServer.Telemetry { public class TelemetryProvider : ITelemetryProvider { - public ILanguageServer? LanguageServer { get; set; } + private readonly ILanguageServerFacade server; - public void PostEvent(TelemetryEvent telemetryEvent) + public TelemetryProvider(ILanguageServerFacade server) + { + this.server = server; + } + + public void PostEvent(BicepTelemetryEvent telemetryEvent) { if (telemetryEvent is null || - !string.IsNullOrWhiteSpace(telemetryEvent.EventName) || + string.IsNullOrWhiteSpace(telemetryEvent.EventName) || telemetryEvent.Properties?.Count == 0) { return; } - LanguageServer?.Window.SendTelemetryEvent(telemetryEvent); + server.Window.SendTelemetryEvent(telemetryEvent); } } }