From 4941ebe65188b76de7104a3dcc585a68013684ce Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Thu, 20 May 2021 14:18:47 -0700 Subject: [PATCH 01/26] Initial changes --- src/Bicep.Core/CoreResources.Designer.cs | 64 +++++++++---------- .../BicepCompletionProviderTests.cs | 17 ++--- .../Completions/BicepCompletionProvider.cs | 25 +++++++- .../Handlers/BicepTelemetryHandler.cs | 44 +++++++++++++ src/Bicep.LangServer/Program.cs | 4 +- src/Bicep.LangServer/Server.cs | 13 +++- .../Telemetry/ITelemetryEventBuilder.cs | 12 ++++ .../Telemetry/ITelemetryProvider.cs | 12 ++++ .../Telemetry/TelemetryEvent.cs | 35 ++++++++++ .../Telemetry/TelemetryProvider.cs | 28 ++++++++ 10 files changed, 210 insertions(+), 44 deletions(-) create mode 100644 src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs create mode 100644 src/Bicep.LangServer/Telemetry/ITelemetryEventBuilder.cs create mode 100644 src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs create mode 100644 src/Bicep.LangServer/Telemetry/TelemetryEvent.cs create mode 100644 src/Bicep.LangServer/Telemetry/TelemetryProvider.cs diff --git a/src/Bicep.Core/CoreResources.Designer.cs b/src/Bicep.Core/CoreResources.Designer.cs index d714694ceed..20ae1e82cad 100644 --- a/src/Bicep.Core/CoreResources.Designer.cs +++ b/src/Bicep.Core/CoreResources.Designer.cs @@ -10,8 +10,8 @@ namespace Bicep.Core { using System; - - + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -23,15 +23,15 @@ namespace Bicep.Core { [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class CoreResources { - + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal CoreResources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// @@ -45,7 +45,7 @@ internal CoreResources() { return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. @@ -59,16 +59,16 @@ internal CoreResources() { resourceCulture = value; } } - + /// - /// Looks up a localized string similar to Custom bicepconfig.json file found [{0}].. + /// Looks up a localized string similar to Custom bicepconfig.json file found ({0}).. /// internal static string BicepConfigCustomSettingsFoundFormatMessage { get { return ResourceManager.GetString("BicepConfigCustomSettingsFoundFormatMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to No bicepconfig.json found for configuration override.. /// @@ -77,99 +77,99 @@ internal static string BicepConfigNoCustomSettingsMessage { return ResourceManager.GetString("BicepConfigNoCustomSettingsMessage", resourceCulture); } } - + /// - /// Looks up a localized string similar to Environment URLs should not be hardcoded. Access URLs via the environment() function to keep references current.. + /// Looks up a localized string similar to Environment URLs should not be hardcoded. Use the environment() function to ensure compatibility across clouds.. /// internal static string EnvironmentUrlHardcodedRuleDescription { get { return ResourceManager.GetString("EnvironmentUrlHardcodedRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Dynamic variable should not use concat - string interpolation should be used.. + /// Looks up a localized string similar to Use string interpolation instead of the concat function.. /// internal static string InterpolateNotConcatRuleDescription { get { return ResourceManager.GetString("InterpolateNotConcatRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Linter is disabled in settings file [{0}]. + /// Looks up a localized string similar to Linter is disabled in settings file located at {0} . /// internal static string LinterDisabledFormatMessage { get { return ResourceManager.GetString("LinterDisabledFormatMessage", resourceCulture); } } - + /// - /// Looks up a localized string similar to Analyzer '{0}' encountered an unexpected exception: {1} + /// Looks up a localized string similar to Analyzer '{0}' encountered an unexpected exception. {1}. /// internal static string LinterRuleExceptionMessageFormat { get { return ResourceManager.GetString("LinterRuleExceptionMessageFormat", resourceCulture); } } - + /// - /// Looks up a localized string similar to Best practice dictates that location be through a parameter that has no default or defaults to 'global' or resourceGroup().location.. + /// Looks up a localized string similar to Resource location should be specified by a parameter with no default value or one that defaults to 'global' or resourceGroup().location.. /// internal static string LocationSetByParameterRuleDescription { get { return ResourceManager.GetString("LocationSetByParameterRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Declared parameter must be referenced within the document scope.. + /// Looks up a localized string similar to Parameter is declared but never used.. /// internal static string ParameterMustBeUsedRuleDescription { get { return ResourceManager.GetString("ParameterMustBeUsedRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Secure parameters can't have hardcoded default. This prevents storage of sensitive data in the Bicep declaration.. + /// Looks up a localized string similar to Secure parameters should not have hardcoded defaults (except for empty or newGuid()).. /// internal static string SecureParameterDefaultRuleDescription { get { return ResourceManager.GetString("SecureParameterDefaultRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to [See : {0}]. + /// Looks up a localized string similar to See {0}. /// internal static string SeeDocLinkFormat { get { return ResourceManager.GetString("SeeDocLinkFormat", resourceCulture); } } - + /// - /// Looks up a localized string similar to String interpolation can be simplified. String variables can be directly assigned to string properties and variables.. + /// Looks up a localized string similar to Remove unnecessary string interpolation.. /// internal static string SimplifyInterpolationRuleDescription { get { return ResourceManager.GetString("SimplifyInterpolationRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Best Practice: remove unnecessary dependsOn.. + /// Looks up a localized string similar to Remove unnecessary dependsOn.. /// internal static string UnnecessaryDependsOnRuleDescription { get { return ResourceManager.GetString("UnnecessaryDependsOnRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Declared variable encountered that is not used within scope.. + /// Looks up a localized string similar to Variable is declared but never used.. /// internal static string UnusedVariableRuleDescription { get { diff --git a/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs b/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs index 6151b47c61b..ff83fa69792 100644 --- a/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs +++ b/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs @@ -17,6 +17,7 @@ using Bicep.LangServer.UnitTests.Completions; using Bicep.LanguageServer.Completions; using Bicep.LanguageServer.Snippets; +using Bicep.LanguageServer.Telemetry; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -34,7 +35,7 @@ public void DeclarationContextShouldReturnKeywordCompletions() var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), grouping); compilation.GetEntrypointSemanticModel().GetAllDiagnostics().Should().BeEmpty(); - var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); var completions = provider.GetFilteredCompletions(compilation, BicepCompletionContext.Create(compilation, 0)); @@ -114,7 +115,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()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); var context = BicepCompletionContext.Create(compilation, offset); var completions = provider.GetFilteredCompletions(compilation, context).ToList(); @@ -153,7 +154,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()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); var completions = provider.GetFilteredCompletions( compilation, BicepCompletionContext.Create(compilation, offset)).ToList(); @@ -186,7 +187,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()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); var context = BicepCompletionContext.Create(compilation, offset); var completions = provider.GetFilteredCompletions(compilation, context).ToList(); @@ -228,7 +229,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()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Type.Span.Position; @@ -245,7 +246,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()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Type.Span.Position; @@ -292,7 +293,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()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Type.Span.Position; @@ -350,7 +351,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()); + var provider = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider()); var offset = codeFragment.IndexOf('|'); diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index 211b6a42501..bd69861fbd6 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -17,6 +17,7 @@ using Bicep.Core.TypeSystem; using Bicep.LanguageServer.Extensions; using Bicep.LanguageServer.Snippets; +using Bicep.LanguageServer.Telemetry; using OmniSharp.Extensions.LanguageServer.Protocol.Models; using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; using SymbolKind = Bicep.Core.Semantics.SymbolKind; @@ -33,11 +34,13 @@ public class BicepCompletionProvider : ICompletionProvider private IFileResolver FileResolver; private readonly ISnippetsProvider SnippetsProvider; + private readonly ITelemetryProvider TelemetryProvider; - public BicepCompletionProvider(IFileResolver fileResolver, ISnippetsProvider snippetsProvider) + public BicepCompletionProvider(IFileResolver fileResolver, ISnippetsProvider snippetsProvider, ITelemetryProvider telemetryProvider) { this.FileResolver = fileResolver; this.SnippetsProvider = snippetsProvider; + this.TelemetryProvider = telemetryProvider; } public IEnumerable GetFilteredCompletions(Compilation compilation, BicepCompletionContext context) @@ -217,7 +220,7 @@ private IEnumerable GetResourceTypeCompletions(SemanticModel mod .Select((reference, index) => CreateResourceTypeCompletion(reference, index, context.ReplacementRange, showApiVersion: true)) .ToList(); } - + // if we do not have the namespace and type notation, we only return uniquie resource types without their api-versions // we need to ensure that Microsoft.Compute/virtualMachines comes before Microsoft.Compute/virtualMachines/extensions // we still order by apiVersion first to have consistent indexes @@ -396,10 +399,15 @@ private IEnumerable CreateResourceBodyCompletions(SemanticModel foreach (Snippet snippet in snippets) { + ITelemetryEventBuilder telemetryEventBuilder = TelemetryProvider.BuildTelemetryEvent("snippet-insertion"); + telemetryEventBuilder.Set("label", snippet!.Prefix); + Command command = Command.Create("bicep.telemetry", telemetryEventBuilder.ToEvent()); + yield return CreateContextualSnippetCompletion(snippet!.Prefix, snippet.Detail, snippet.Text, context.ReplacementRange, + command: command, snippet.CompletionPriority, preselect: true); } @@ -985,6 +993,19 @@ private static CompletionItem CreateContextualSnippetCompletion(string label, st .WithSortText(GetSortText(label, priority)) .Build(); + /// + /// Creates a completion with a contextual snippet. This will look like a snippet to the user. + /// + private static CompletionItem CreateContextualSnippetCompletion(string label, string detail, string snippet, Range replacementRange, Command command, CompletionPriority priority = CompletionPriority.Medium, bool preselect = false) => + CompletionItemBuilder.Create(CompletionItemKind.Snippet) + .WithLabel(label) + .WithSnippetEdit(replacementRange, snippet) + .WithDetail(detail) + .WithDocumentation($"```bicep\n{new Snippet(snippet).FormatDocumentation()}\n```") + .WithSortText(GetSortText(label, priority)) + .WithCommand(command) + .Preselect(preselect); + private static CompletionItem CreateSymbolCompletion(Symbol symbol, Range replacementRange, string? insertText = null) { insertText ??= symbol.Name; diff --git a/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs new file mode 100644 index 00000000000..87a7b4e730b --- /dev/null +++ b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs @@ -0,0 +1,44 @@ +// 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.LanguageServer.Protocol.Workspace; + +namespace Bicep.LanguageServer.Handlers +{ + public class BicepTelemetryHandler : ExecuteCommandHandler + { + private readonly ITelemetryProvider TelemetryProvider; + + public BicepTelemetryHandler(ITelemetryProvider telemetryProvider) + : base(GetExecuteCommandRegistrationOptions()) + { + TelemetryProvider = telemetryProvider; + } + + private static ExecuteCommandRegistrationOptions GetExecuteCommandRegistrationOptions() + => new ExecuteCommandRegistrationOptions() + { + Commands = new Container("bicep.telemetry") + }; + + public override Task Handle(ExecuteCommandParams request, 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); + } + + return Unit.Task; + } + } +} diff --git a/src/Bicep.LangServer/Program.cs b/src/Bicep.LangServer/Program.cs index 1a580563ee5..9ef10d041f1 100644 --- a/src/Bicep.LangServer/Program.cs +++ b/src/Bicep.LangServer/Program.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Bicep.Core.FileSystem; using Bicep.Core.TypeSystem.Az; +using Bicep.LanguageServer.Telemetry; namespace Bicep.LanguageServer { @@ -22,6 +23,7 @@ public static async Task Main(string[] args) { ResourceTypeProvider = AzResourceTypeProvider.CreateWithAzTypes(), FileResolver = new FileResolver(), + TelemetryProvider = new TelemetryProvider() }); await server.RunAsync(cancellationToken); @@ -52,4 +54,4 @@ private static async Task RunWithCancellationAsync(Func } } } -} \ No newline at end of file +} diff --git a/src/Bicep.LangServer/Server.cs b/src/Bicep.LangServer/Server.cs index 0c45b410b8f..8d22296183b 100644 --- a/src/Bicep.LangServer/Server.cs +++ b/src/Bicep.LangServer/Server.cs @@ -15,6 +15,7 @@ using Bicep.LanguageServer.Handlers; using Bicep.LanguageServer.Providers; using Bicep.LanguageServer.Snippets; +using Bicep.LanguageServer.Telemetry; using Microsoft.Extensions.DependencyInjection; using OmniSharp.Extensions.LanguageServer.Protocol.Client; using OmniSharp.Extensions.LanguageServer.Protocol.Window; @@ -32,6 +33,8 @@ public class CreationOptions public IResourceTypeProvider? ResourceTypeProvider { get; set; } public IFileResolver? FileResolver { get; set; } + + public ITelemetryProvider? TelemetryProvider { get; set; } } private readonly OmnisharpLanguageServer server; @@ -66,10 +69,17 @@ private Server(CreationOptions creationOptions, Action on .WithHandler() .WithHandler() .WithHandler() +#pragma warning restore 0612 + .WithHandler() .WithServices(services => RegisterServices(creationOptions, services)); onOptionsFunc(options); }); + + if (creationOptions.TelemetryProvider is TelemetryProvider telemetryProvider) + { + telemetryProvider.LanguageServer = server; + } } public async Task RunAsync(CancellationToken cancellationToken) @@ -79,13 +89,14 @@ public async Task RunAsync(CancellationToken cancellationToken) await server.WaitForExit; } - private static void RegisterServices(CreationOptions creationOptions, IServiceCollection services) + private void RegisterServices(CreationOptions creationOptions, IServiceCollection services) { // using type based registration so dependencies can be injected automatically // without manually constructing up the graph 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(); diff --git a/src/Bicep.LangServer/Telemetry/ITelemetryEventBuilder.cs b/src/Bicep.LangServer/Telemetry/ITelemetryEventBuilder.cs new file mode 100644 index 00000000000..f0a5bf51605 --- /dev/null +++ b/src/Bicep.LangServer/Telemetry/ITelemetryEventBuilder.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Bicep.LanguageServer.Telemetry +{ + public interface ITelemetryEventBuilder + { + ITelemetryEventBuilder Set(string propertyName, object value); + + TelemetryEvent ToEvent(); + } +} diff --git a/src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs b/src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs new file mode 100644 index 00000000000..edf8f59a691 --- /dev/null +++ b/src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Bicep.LanguageServer.Telemetry +{ + public interface ITelemetryProvider + { + ITelemetryEventBuilder BuildTelemetryEvent(string eventName); + + void PostEvent(TelemetryEvent telemetryEvent); + } +} diff --git a/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs b/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs new file mode 100644 index 00000000000..1db23e4f24b --- /dev/null +++ b/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; + +namespace Bicep.LanguageServer.Telemetry +{ + public class TelemetryEvent : TelemetryEventParams, ITelemetryEventBuilder + { + private readonly Dictionary _properties = new Dictionary(); + + public TelemetryEvent(string eventName) + { + EventName = eventName; + } + + public string EventName { get; set; } + public Dictionary? Properties => _properties; + + public ITelemetryEventBuilder Set(string propertyName, object value) + { + AddProperty(propertyName, value); + + return this; + } + + private void AddProperty(string key, object value) + { + _properties[key] = value; + } + + public TelemetryEvent ToEvent() => this; + } +} diff --git a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs new file mode 100644 index 00000000000..a33e92538ee --- /dev/null +++ b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using OmniSharp.Extensions.LanguageServer.Protocol.Server; +using OmniSharp.Extensions.LanguageServer.Protocol.Window; + +namespace Bicep.LanguageServer.Telemetry +{ + public class TelemetryProvider : ITelemetryProvider + { + public ILanguageServer? LanguageServer { get; set; } + + public ITelemetryEventBuilder BuildTelemetryEvent(string eventName) + { + return new TelemetryEvent(eventName); + } + + public void PostEvent(TelemetryEvent telemetryEvent) + { + if (telemetryEvent == null || telemetryEvent.Properties?.Count == 0) + { + return; + } + + LanguageServer?.Window.SendTelemetryEvent(telemetryEvent); + } + } +} From bbbcc6e01dbce9d654cd84aea1cc7fd90c25b802 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Thu, 20 May 2021 14:43:06 -0700 Subject: [PATCH 02/26] Code cleanup --- .../Completions/BicepCompletionProvider.cs | 26 ++++++------------- .../Telemetry/ITelemetryEventBuilder.cs | 12 --------- .../Telemetry/ITelemetryProvider.cs | 2 -- .../Telemetry/TelemetryEvent.cs | 25 ++++-------------- .../Telemetry/TelemetryProvider.cs | 5 ---- src/vscode-bicep/.vscode/launch.json | 4 ++- 6 files changed, 16 insertions(+), 58 deletions(-) delete mode 100644 src/Bicep.LangServer/Telemetry/ITelemetryEventBuilder.cs diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index bd69861fbd6..35447ef8f25 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -399,17 +399,20 @@ private IEnumerable CreateResourceBodyCompletions(SemanticModel foreach (Snippet snippet in snippets) { - ITelemetryEventBuilder telemetryEventBuilder = TelemetryProvider.BuildTelemetryEvent("snippet-insertion"); - telemetryEventBuilder.Set("label", snippet!.Prefix); - Command command = Command.Create("bicep.telemetry", telemetryEventBuilder.ToEvent()); + Dictionary properties = new Dictionary() + { + { "label", snippet!.Prefix } + }; + TelemetryEvent telemetryEvent = new("snippet-insertion", properties); + Command command = Command.Create("bicep.telemetry", telemetryEvent); yield return CreateContextualSnippetCompletion(snippet!.Prefix, snippet.Detail, snippet.Text, context.ReplacementRange, - command: command, snippet.CompletionPriority, - preselect: true); + preselect: true, + command: command); } } } @@ -993,19 +996,6 @@ private static CompletionItem CreateContextualSnippetCompletion(string label, st .WithSortText(GetSortText(label, priority)) .Build(); - /// - /// Creates a completion with a contextual snippet. This will look like a snippet to the user. - /// - private static CompletionItem CreateContextualSnippetCompletion(string label, string detail, string snippet, Range replacementRange, Command command, CompletionPriority priority = CompletionPriority.Medium, bool preselect = false) => - CompletionItemBuilder.Create(CompletionItemKind.Snippet) - .WithLabel(label) - .WithSnippetEdit(replacementRange, snippet) - .WithDetail(detail) - .WithDocumentation($"```bicep\n{new Snippet(snippet).FormatDocumentation()}\n```") - .WithSortText(GetSortText(label, priority)) - .WithCommand(command) - .Preselect(preselect); - private static CompletionItem CreateSymbolCompletion(Symbol symbol, Range replacementRange, string? insertText = null) { insertText ??= symbol.Name; diff --git a/src/Bicep.LangServer/Telemetry/ITelemetryEventBuilder.cs b/src/Bicep.LangServer/Telemetry/ITelemetryEventBuilder.cs deleted file mode 100644 index f0a5bf51605..00000000000 --- a/src/Bicep.LangServer/Telemetry/ITelemetryEventBuilder.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Bicep.LanguageServer.Telemetry -{ - public interface ITelemetryEventBuilder - { - ITelemetryEventBuilder Set(string propertyName, object value); - - TelemetryEvent ToEvent(); - } -} diff --git a/src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs b/src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs index edf8f59a691..59e7dd1da02 100644 --- a/src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs +++ b/src/Bicep.LangServer/Telemetry/ITelemetryProvider.cs @@ -5,8 +5,6 @@ namespace Bicep.LanguageServer.Telemetry { public interface ITelemetryProvider { - ITelemetryEventBuilder BuildTelemetryEvent(string eventName); - void PostEvent(TelemetryEvent telemetryEvent); } } diff --git a/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs b/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs index 1db23e4f24b..5643ae65822 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs +++ b/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs @@ -6,30 +6,15 @@ namespace Bicep.LanguageServer.Telemetry { - public class TelemetryEvent : TelemetryEventParams, ITelemetryEventBuilder + public class TelemetryEvent : TelemetryEventParams { - private readonly Dictionary _properties = new Dictionary(); - - public TelemetryEvent(string eventName) - { - EventName = eventName; - } - public string EventName { get; set; } - public Dictionary? Properties => _properties; + public Dictionary? Properties { get; set; } - public ITelemetryEventBuilder Set(string propertyName, object value) + public TelemetryEvent(string eventName, Dictionary? properties) { - AddProperty(propertyName, value); - - return this; - } - - private void AddProperty(string key, object value) - { - _properties[key] = value; + EventName = eventName; + Properties = properties; } - - public TelemetryEvent ToEvent() => this; } } diff --git a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs index a33e92538ee..6e2544bd971 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs +++ b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs @@ -10,11 +10,6 @@ public class TelemetryProvider : ITelemetryProvider { public ILanguageServer? LanguageServer { get; set; } - public ITelemetryEventBuilder BuildTelemetryEvent(string eventName) - { - return new TelemetryEvent(eventName); - } - public void PostEvent(TelemetryEvent telemetryEvent) { if (telemetryEvent == null || telemetryEvent.Properties?.Count == 0) diff --git a/src/vscode-bicep/.vscode/launch.json b/src/vscode-bicep/.vscode/launch.json index 86646cd1af6..fcccc0246ab 100644 --- a/src/vscode-bicep/.vscode/launch.json +++ b/src/vscode-bicep/.vscode/launch.json @@ -2,6 +2,7 @@ { "version": "0.2.0", "configurations": [ + { "name": "Launch Extension", "type": "extensionHost", @@ -16,7 +17,8 @@ "outFiles": ["${workspaceRoot}/out/src/**/*.js"], "preLaunchTask": "${defaultBuildTask}", "env": { - "BICEP_LANGUAGE_SERVER_PATH": "${workspaceRoot}/../Bicep.LangServer/bin/Debug/net5.0/Bicep.LangServer.dll" + "BICEP_LANGUAGE_SERVER_PATH": "${workspaceRoot}/../Bicep.LangServer/bin/Debug/net5.0/Bicep.LangServer.dll", + "DEBUGTELEMETRY": "verbose" } }, { From d5815acf7fd9273157fb0d93a839e41fe116b427 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Thu, 20 May 2021 14:58:27 -0700 Subject: [PATCH 03/26] Moved telemetry to declaration snippet insertion --- .../Completions/BicepCompletionProvider.cs | 20 +++++++++---------- .../Telemetry/TelemetryProvider.cs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index 35447ef8f25..bc2fb5e77c7 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -85,11 +85,19 @@ private IEnumerable GetDeclarationCompletions(BicepCompletionCon foreach (Snippet resourceSnippet in SnippetsProvider.GetTopLevelNamedDeclarationSnippets()) { + Dictionary properties = new Dictionary() + { + { "label", resourceSnippet.Prefix } + }; + TelemetryEvent telemetryEvent = new("declaration-snippet-completion", properties); + Command command = Command.Create("bicep.telemetry", telemetryEvent); + yield return CreateContextualSnippetCompletion(resourceSnippet.Prefix, resourceSnippet.Detail, resourceSnippet.Text, context.ReplacementRange, - resourceSnippet.CompletionPriority); + resourceSnippet.CompletionPriority, + command: command); } } @@ -399,20 +407,12 @@ private IEnumerable CreateResourceBodyCompletions(SemanticModel foreach (Snippet snippet in snippets) { - Dictionary properties = new Dictionary() - { - { "label", snippet!.Prefix } - }; - TelemetryEvent telemetryEvent = new("snippet-insertion", properties); - Command command = Command.Create("bicep.telemetry", telemetryEvent); - yield return CreateContextualSnippetCompletion(snippet!.Prefix, snippet.Detail, snippet.Text, context.ReplacementRange, snippet.CompletionPriority, - preselect: true, - command: command); + preselect: true); } } } diff --git a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs index 6e2544bd971..63467ef52e0 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs +++ b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs @@ -12,7 +12,7 @@ public class TelemetryProvider : ITelemetryProvider public void PostEvent(TelemetryEvent telemetryEvent) { - if (telemetryEvent == null || telemetryEvent.Properties?.Count == 0) + if (telemetryEvent is null || telemetryEvent.Properties?.Count == 0) { return; } From 3c181e193ddce2e765675219df54e88b5f96f9e6 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Thu, 20 May 2021 15:21:23 -0700 Subject: [PATCH 04/26] Updated readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a654d55b35a..82e2abb0018 100644 --- a/README.md +++ b/README.md @@ -126,5 +126,9 @@ Because we are now treating the ARM Template as an IL, we expect and encourage o * [Farmer](https://compositionalit.github.io/farmer/) (@isaacabraham) - Generate and deploy ARM Templates on .NET * [Cloud Maker](https://cloudmaker.ai) (@cloud-maker-ai) - Draw deployable infrastructure diagrams that are converted to ARM templates +## Telemetry + +VS Code collects usage data and sends it to Microsoft to help improve our products and services. Read our [privacy statement](https://go.microsoft.com/fwlink/?LinkID=528096&clcid=0x409) to learn more. If you don’t wish to send usage data to Microsoft, you can set the `telemetry.enableTelemetry` setting to `false`. Learn more in our [FAQ](https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting). + ## Contributing See [Contributing to Bicep](./CONTRIBUTING.md) for information on building/running the code, contributing code, contributing examples and contributing feature requests or bug reports. From 0ef1a980ab732cdfa99370b6416254b49bdc1170 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Thu, 20 May 2021 17:08:52 -0700 Subject: [PATCH 05/26] Added integration tests --- .../TelemetryTests.cs | 47 +++++++++++++++++++ .../Completions/BicepCompletionProvider.cs | 4 +- .../Telemetry/TelemetryConstants.cs | 16 +++++++ 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs create mode 100644 src/Bicep.LangServer/Telemetry/TelemetryConstants.cs diff --git a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs new file mode 100644 index 00000000000..fd5963f8497 --- /dev/null +++ b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; +using Bicep.Core.Syntax; +using Bicep.Core.Text; +using Bicep.Core.UnitTests.Assertions; +using Bicep.LanguageServer.Telemetry; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; + +namespace Bicep.LangServer.IntegrationTests +{ + [TestClass] + [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "Test methods do not need to follow this convention.")] + public class TelemetryTests + { + [NotNull] + public TestContext? TestContext { get; set; } + + [TestMethod] + public async Task ValidateDeclarationSnippetCompletionItemContainsCommandWithTelemetryInformation() + { + var syntaxTree = SyntaxTree.Create(new Uri("file:///main.bicep"), string.Empty); + var client = await IntegrationTestHelper.StartServerWithTextAsync(string.Empty, syntaxTree.FileUri); + + var completions = await client.RequestCompletion(new CompletionParams + { + TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri), + Position = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, 0), + }); + + CompletionItem completionItem = completions.Where(x => x.Kind == CompletionItemKind.Snippet && x.Label == "res-aks-cluster").First(); + Command? command = completionItem.Command; + TelemetryEvent? telemetryEvent = command?.Arguments?.First().ToObject(); + + command?.Name.Should().Be(TelemetryConstants.CommandName); + telemetryEvent?.EventName.Should().Be(TelemetryConstants.EventNames.DeclarationSnippetCompletion); + telemetryEvent?.Properties?.ContainsKey("label"); + } + } +} diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index bc2fb5e77c7..8f0139f1bdd 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -89,8 +89,8 @@ private IEnumerable GetDeclarationCompletions(BicepCompletionCon { { "label", resourceSnippet.Prefix } }; - TelemetryEvent telemetryEvent = new("declaration-snippet-completion", properties); - Command command = Command.Create("bicep.telemetry", telemetryEvent); + TelemetryEvent telemetryEvent = new(TelemetryConstants.EventNames.DeclarationSnippetCompletion, properties); + Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); yield return CreateContextualSnippetCompletion(resourceSnippet.Prefix, resourceSnippet.Detail, diff --git a/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs b/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs new file mode 100644 index 00000000000..bc8dec8dcfd --- /dev/null +++ b/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Bicep.LanguageServer.Telemetry +{ + public class TelemetryConstants + { + public const string Prefix = "bicep/"; + public const string CommandName = "bicep.Telemetry"; + + public class EventNames + { + public const string DeclarationSnippetCompletion = Prefix + nameof(DeclarationSnippetCompletion); + } + } +} From 881e0391eb0411c942cf30d570d80fd905842ce1 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Fri, 21 May 2021 11:34:01 -0700 Subject: [PATCH 06/26] Code cleanup --- src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs | 2 +- src/Bicep.LangServer/Completions/BicepCompletionProvider.cs | 2 +- src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs | 2 +- src/Bicep.LangServer/Telemetry/TelemetryConstants.cs | 3 +-- src/vscode-bicep/.vscode/launch.json | 3 +-- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs index fd5963f8497..7320b7f392f 100644 --- a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs @@ -40,7 +40,7 @@ public async Task ValidateDeclarationSnippetCompletionItemContainsCommandWithTel TelemetryEvent? telemetryEvent = command?.Arguments?.First().ToObject(); command?.Name.Should().Be(TelemetryConstants.CommandName); - telemetryEvent?.EventName.Should().Be(TelemetryConstants.EventNames.DeclarationSnippetCompletion); + telemetryEvent?.EventName.Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); telemetryEvent?.Properties?.ContainsKey("label"); } } diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index 8f0139f1bdd..dce674e8994 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -89,7 +89,7 @@ private IEnumerable GetDeclarationCompletions(BicepCompletionCon { { "label", resourceSnippet.Prefix } }; - TelemetryEvent telemetryEvent = new(TelemetryConstants.EventNames.DeclarationSnippetCompletion, properties); + TelemetryEvent telemetryEvent = new(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion, properties); Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); yield return CreateContextualSnippetCompletion(resourceSnippet.Prefix, diff --git a/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs index 87a7b4e730b..45344f430cd 100644 --- a/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs @@ -25,7 +25,7 @@ public BicepTelemetryHandler(ITelemetryProvider telemetryProvider) private static ExecuteCommandRegistrationOptions GetExecuteCommandRegistrationOptions() => new ExecuteCommandRegistrationOptions() { - Commands = new Container("bicep.telemetry") + Commands = new Container(TelemetryConstants.CommandName) }; public override Task Handle(ExecuteCommandParams request, CancellationToken cancellationToken) diff --git a/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs b/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs index bc8dec8dcfd..a1600013fa5 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs +++ b/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs @@ -5,12 +5,11 @@ namespace Bicep.LanguageServer.Telemetry { public class TelemetryConstants { - public const string Prefix = "bicep/"; public const string CommandName = "bicep.Telemetry"; public class EventNames { - public const string DeclarationSnippetCompletion = Prefix + nameof(DeclarationSnippetCompletion); + public const string TopLevelDeclarationSnippetInsertion = nameof(TopLevelDeclarationSnippetInsertion); } } } diff --git a/src/vscode-bicep/.vscode/launch.json b/src/vscode-bicep/.vscode/launch.json index fcccc0246ab..b0a31f07f31 100644 --- a/src/vscode-bicep/.vscode/launch.json +++ b/src/vscode-bicep/.vscode/launch.json @@ -17,8 +17,7 @@ "outFiles": ["${workspaceRoot}/out/src/**/*.js"], "preLaunchTask": "${defaultBuildTask}", "env": { - "BICEP_LANGUAGE_SERVER_PATH": "${workspaceRoot}/../Bicep.LangServer/bin/Debug/net5.0/Bicep.LangServer.dll", - "DEBUGTELEMETRY": "verbose" + "BICEP_LANGUAGE_SERVER_PATH": "${workspaceRoot}/../Bicep.LangServer/bin/Debug/net5.0/Bicep.LangServer.dll" } }, { From 17a95d7031c4f1de7974aaa0ea34178870f71f9c Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Fri, 21 May 2021 12:03:37 -0700 Subject: [PATCH 07/26] Fixed integration test --- .../Files/Completions/declarations.json | 726 ++++++++++++++++++ .../TelemetryTests.cs | 2 +- .../Completions/BicepCompletionProvider.cs | 2 +- src/vscode-bicep/.vscode/launch.json | 1 - 4 files changed, 728 insertions(+), 3 deletions(-) diff --git a/src/Bicep.Core.Samples/Files/Completions/declarations.json b/src/Bicep.Core.Samples/Files/Completions/declarations.json index 6c5b9069678..af9a21ddb15 100644 --- a/src/Bicep.Core.Samples/Files/Completions/declarations.json +++ b/src/Bicep.Core.Samples/Files/Completions/declarations.json @@ -29,6 +29,17 @@ "textEdit": { "range": {}, "newText": "module ${1:Identifier} ${2:Path} = {\n name: $3\n $0\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "module" + } + } + ] } }, { @@ -61,6 +72,17 @@ "textEdit": { "range": {}, "newText": "output ${1:Identifier} ${2:Type} = $0" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "output" + } + } + ] } }, { @@ -93,6 +115,17 @@ "textEdit": { "range": {}, "newText": "param ${1:Identifier} ${2:Type}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "param" + } + } + ] } }, { @@ -111,6 +144,17 @@ "textEdit": { "range": {}, "newText": "param ${1:Identifier} ${2:Type} = ${3:DefaultValue}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "param-defaults" + } + } + ] } }, { @@ -129,6 +173,17 @@ "textEdit": { "range": {}, "newText": "@allowed([\n $3\n])\nparam ${1:Identifier} ${2:Type} = $4" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "param-defaults-allowed-values" + } + } + ] } }, { @@ -147,6 +202,17 @@ "textEdit": { "range": {}, "newText": "@secure()\nparam ${1:Identifier} string" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "param-secure-string" + } + } + ] } }, { @@ -165,6 +231,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:aksCluster} 'Microsoft.ContainerService/managedClusters@2021-03-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n identity: {\n type: 'SystemAssigned'\n }\n properties: {\n kubernetesVersion: '${3|1.19.7,1.19.6,1.18.14,1.18.10,1.17.16,1.17.13|}'\n dnsPrefix: ${4:'dnsprefix'}\n enableRBAC: true\n agentPoolProfiles: [\n {\n name: 'agentpool'\n count: ${5:3}\n vmSize: ${6:'Standard_DS2_v2'}\n osType: 'Linux'\n mode: 'System'\n }\n ]\n linuxProfile: {\n adminUsername: ${7:'adminUserName'}\n ssh: {\n publicKeys: [\n {\n keyData: ${8:'REQUIRED'}\n }\n ]\n }\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-aks-cluster" + } + } + ] } }, { @@ -201,6 +278,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:appServicePlan} 'Microsoft.Web/serverfarms@2020-12-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n sku: {\n name: 'F1'\n capacity: 1\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-app-plan" + } + } + ] } }, { @@ -219,6 +307,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:applicationSecurityGroup} 'Microsoft.Network/applicationSecurityGroups@2020-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-app-security-group" + } + } + ] } }, { @@ -237,6 +336,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:automationAccount} 'Microsoft.Automation/automationAccounts@2019-06-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n sku: {\n name: ${3|'Free','Basic'|}\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-automation-account" + } + } + ] } }, { @@ -255,6 +365,17 @@ "textEdit": { "range": {}, "newText": "resource automationAccount 'Microsoft.Automation/automationAccounts@2019-06-01' = {\n name: ${1:'name'}\n}\n\nresource ${2:automationCertificate} 'Microsoft.Automation/automationAccounts/certificates@2019-06-01' = {\n parent: automationAccount\n name: ${3:'name'}\n properties: {\n base64Value: ${4:'base64Value'}\n description: ${5:'description'}\n thumbprint: ${6:'thumbprint'}\n isExportable: ${7|true,false|}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-automation-cert" + } + } + ] } }, { @@ -273,6 +394,17 @@ "textEdit": { "range": {}, "newText": "resource automationAccount 'Microsoft.Automation/automationAccounts@2019-06-01' = {\n name: ${1:'name'}\n}\n\nresource ${2:automationCredential} 'Microsoft.Automation/automationAccounts/credentials@2019-06-01' = {\n parent: automationAccount\n name: ${3:'name'}\n properties: {\n userName: ${4:'userName'}\n password: ${5:'password'}\n description: ${6:'description'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-automation-cred" + } + } + ] } }, { @@ -291,6 +423,17 @@ "textEdit": { "range": {}, "newText": "resource automationAccount 'Microsoft.Automation/automationAccounts@2019-06-01' = {\n name: ${1:'name'}\n}\n\nresource ${2:automationJobSchedule} 'Microsoft.Automation/automationAccounts/jobSchedules@2019-06-01' = {\n parent: automationAccount\n name: ${3:'name'}\n properties: {\n schedule: {\n name: ${4:'name'}\n }\n runbook: {\n name: ${5:'name'}\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-automation-job-schedule" + } + } + ] } }, { @@ -309,6 +452,17 @@ "textEdit": { "range": {}, "newText": "resource automationAccount 'Microsoft.Automation/automationAccounts@2015-10-31' = {\n name: ${1:'name'}\n}\n\nresource ${2:automationAccountVariable} 'Microsoft.Automation/automationAccounts/modules@2015-10-31' = {\n parent: automationAccount\n name: ${3:'name'}\n properties: {\n contentLink: {\n uri: ${4:'https://content-url.nupkg'}\n }\n }\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-automation-module" + } + } + ] } }, { @@ -327,6 +481,17 @@ "textEdit": { "range": {}, "newText": "resource automationAccount 'Microsoft.Automation/automationAccounts@2019-06-01' = {\n name: ${1:'name'}\n}\n\nresource ${2:automationRunbook} 'Microsoft.Automation/automationAccounts/runbooks@2019-06-01' = {\n parent: automationAccount\n name: ${3:'name'}\n location: resourceGroup().location\n properties: {\n logVerbose: ${4|true,false|}\n logProgress: ${5|true,false|}\n runbookType: '${6|Script,Graph,PowerShellWorkflow,PowerShell,GraphPowerShellWorkflow,GraphPowerShell|}'\n publishContentLink: {\n uri: ${7:'uri'}\n version: ${8:'1.0.0.0'}\n }\n description: ${9:'description'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-automation-runbook" + } + } + ] } }, { @@ -345,6 +510,17 @@ "textEdit": { "range": {}, "newText": "resource automationAccount 'Microsoft.Automation/automationAccounts@2019-06-01' = {\n name: ${1:'name'}\n}\n\nresource ${2:automationSchedule} 'Microsoft.Automation/automationAccounts/schedules@2019-06-01' = {\n parent: automationAccount\n name: ${3:'name'}\n properties: {\n description: ${4:'description'}\n startTime: ${5:'startTime'}\n interval: ${6:'interval'}\n frequency: '${7|OneTime,Day,Hour,Week,Month|}'\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-automation-schedule" + } + } + ] } }, { @@ -363,6 +539,17 @@ "textEdit": { "range": {}, "newText": "resource automationAccount 'Microsoft.Automation/automationAccounts@2019-06-01' = {\n name: ${1:'name'}\n}\n\nresource ${2:automationVariable} 'Microsoft.Automation/automationAccounts/variables@2019-06-01' = {\n parent: automationAccount\n name: ${3:'name'}\n properties: {\n value: ${4:'value'}\n description: ${5:'description'}\n isEncrypted: ${6|true,false|}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-automation-variable" + } + } + ] } }, { @@ -381,6 +568,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:availabilitySet} 'Microsoft.Compute/availabilitySets@2020-12-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-availability-set" + } + } + ] } }, { @@ -399,6 +597,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:containerGroup} 'Microsoft.ContainerInstance/containerGroups@2021-03-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n containers: [\n {\n name: ${3:'containername'}\n properties: {\n image: ${4:'mcr.microsoft.com/azuredocs/aci-helloworld:latest'}\n ports: [\n {\n port: ${5:80}\n }\n ]\n resources: {\n requests: {\n cpu: ${6:1}\n memoryInGB: ${7:4}\n }\n }\n }\n }\n ]\n restartPolicy: ${8|'OnFailure','Always','Never'|}\n osType: ${9|'Linux','Windows'|}\n ipAddress: {\n type: 'Public'\n ports: [\n {\n protocol: ${10|'TCP','UDP'|}\n port: ${11:80}\n }\n ]\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-container-group" + } + } + ] } }, { @@ -417,6 +626,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:containerRegistry} 'Microsoft.ContainerRegistry/registries@2019-05-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n sku: {\n name: ${3|'Classic','Basic','Standard','Premium'|}\n }\n properties: {\n adminUserEnabled: ${4|true,false|}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-container-registry" + } + } + ] } }, { @@ -435,6 +655,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:cosmosDbAccount} 'Microsoft.DocumentDB/databaseAccounts@2021-03-15' = {\n name: ${2:'name'}\n location: resourceGroup().location\n kind: ${3|'GlobalDocumentDB','MongoDB','Parse'|}\n properties: {\n consistencyPolicy: {\n defaultConsistencyLevel: ${4|'Eventual','Session','BoundedStaleness','Strong','ConsistentPrefix'|}\n maxStalenessPrefix: ${5:1}\n maxIntervalInSeconds: ${6:5}\n }\n locations: [\n {\n locationName: ${7:'location'}\n failoverPriority: ${8:0}\n }\n ]\n databaseAccountOfferType: 'Standard'\n enableAutomaticFailover: ${9|true,false|}\n capabilities: [\n {\n name: ${10|'EnableTable','EnableGremlin'|}\n }\n ]\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-cosmos-account" + } + } + ] } }, { @@ -453,6 +684,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:cassandraKeyspace} 'Microsoft.DocumentDB/databaseAccounts/apis/keyspaces@2016-03-31' = {\n name: ${2:'name'}\n properties: {\n resource: {\n id: ${3:'id'}\n }\n options: {\n throughput: ${4:'throughput'}\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-cosmos-cassandra-keyspace" + } + } + ] } }, { @@ -471,6 +713,17 @@ "textEdit": { "range": {}, "newText": "resource cassandraKeyspace 'Microsoft.DocumentDB/databaseAccounts/apis/keyspaces@2016-03-31' = {\n name: ${1:'name'}\n properties: {\n resource: {\n id: ${2:'id'}\n }\n options: {}\n }\n}\n\nresource ${3:cassandraKeyspaceTable} 'Microsoft.DocumentDb/databaseAccounts/apis/keyspaces/tables@2016-03-31' = {\n parent: cassandraKeyspace\n name: ${4:'name'}\n properties: {\n resource: {\n id: ${5:'id'}\n }\n options: {}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-cosmos-cassandra-table" + } + } + ] } }, { @@ -489,6 +742,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:gremlinDb} 'Microsoft.DocumentDB/databaseAccounts/apis/databases@2016-03-31' = {\n name: ${2:'name'}\n properties: {\n resource: {\n id: ${3:'id'}\n }\n options: {\n throughput: ${4:'throughput'}\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-cosmos-gremlin-database" + } + } + ] } }, { @@ -507,6 +771,17 @@ "textEdit": { "range": {}, "newText": "resource gremlinDb 'Microsoft.DocumentDB/databaseAccounts/apis/databases@2016-03-31' = {\n name: ${1:'name'}\n properties: {\n resource: {\n id: ${2:'id'}\n }\n options: {\n throughput: ${3:'throughput'}\n }\n }\n}\n\nresource ${4:cosmosDbGremlinGraph} 'Microsoft.DocumentDb/databaseAccounts/apis/databases/graphs@2016-03-31' = {\n parent: gremlinDb\n name: ${5:'name'}\n properties: {\n resource: {\n id: ${6:'id'}\n partitionKey: {\n paths: [\n ${7:'paths'}\n ]\n kind: '${8|Hash,Range|}'\n }\n indexingPolicy: {\n indexingMode: '${9|Consistent,Lazy,None|}'\n includedPaths: [\n {\n path: ${10:'path'}\n indexes: [\n {\n kind: '${11|Hash,Range,Spatial|}'\n dataType: '${12|String,Number,Point,Polygon,LineString,MultiPolygon|}'\n precision: ${13:-1}\n }\n ]\n }\n ]\n excludedPaths: [\n {\n path: ${14:'path'}\n }\n ]\n }\n }\n options: {\n throughput: ${15:'throughput'}\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-cosmos-gremlin-graph" + } + } + ] } }, { @@ -525,6 +800,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:mongoDb} 'Microsoft.DocumentDB/databaseAccounts/apis/databases@2016-03-31' = {\n name: ${2:'name'}\n properties: {\n resource: {\n id: ${3:'id'}\n }\n options: {}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-cosmos-mongo-database" + } + } + ] } }, { @@ -543,6 +829,17 @@ "textEdit": { "range": {}, "newText": "resource sqlDb 'Microsoft.DocumentDB/databaseAccounts/apis/databases@2016-03-31' = {\n name: ${1:'name'}\n properties: {\n resource: {\n id: ${2:'id'}\n }\n options: {}\n }\n}\n\nresource ${3:sqlContainerName} 'Microsoft.DocumentDb/databaseAccounts/apis/databases/containers@2016-03-31' = {\n parent: sqlDb \n name: ${4:'name'}\n properties: {\n resource: {\n id: ${5:'id'}\n partitionKey: {\n paths: [\n ${6:'paths'}\n ]\n kind: '${7|Hash,Range|}'\n }\n indexingPolicy: {\n indexingMode: '${8|Consistent,Lazy,None|}'\n includedPaths: [\n {\n path: ${9:'path'}\n indexes: [\n {\n kind: '${10|Hash,Range,Spatial|}'\n dataType: '${11|String,Number,Point,Polygon,LineString,MultiPolygon|}'\n precision: ${12:'precision'}\n }\n ]\n }\n ]\n excludedPaths: [\n {\n path: ${13:'path'}\n }\n ]\n }\n }\n options: {}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-cosmos-sql-container" + } + } + ] } }, { @@ -561,6 +858,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:sqlDb} 'Microsoft.DocumentDB/databaseAccounts/apis/databases@2016-03-31' = {\n name: ${2:'name'}\n properties: {\n resource: {\n id: ${3:'id'}\n }\n options: {\n throughput: ${4:'throughput'}\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-cosmos-sql-database" + } + } + ] } }, { @@ -579,6 +887,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:cosmosTable} 'Microsoft.DocumentDB/databaseAccounts/apis/tables@2016-03-31' = {\n name: ${2:'name'}\n properties: {\n resource: {\n id: ${3:'id'}\n }\n options: {\n throughput: ${4:'throughput'}\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-cosmos-tablestorage-table" + } + } + ] } }, { @@ -597,6 +916,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:dataLakeStore} 'Microsoft.DataLakeStore/accounts@2016-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n newTier: ${3|'Consumption','Commitment_1TB','Commitment_10TB','Commitment_100TB','Commitment_500TB','Commitment_1PB','Commitment_5PB'|}\n encryptionState: ${4|'Enabled','Disabled'|}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-data-lake" + } + } + ] } }, { @@ -615,6 +945,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:dnsZone} 'Microsoft.Network/dnsZones@2018-05-01' = {\n name: ${2:'name'}\n location: 'global'\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-dns-zone" + } + } + ] } }, { @@ -651,6 +992,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:publicIPAddress} 'Microsoft.Network/publicIPAddresses@2019-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n publicIPAllocationMethod: 'Dynamic'\n dnsSettings: {\n domainNameLabel: ${3:'dnsname'}\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-ip" + } + } + ] } }, { @@ -669,6 +1021,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:publicIPPrefix} 'Microsoft.Network/publicIPPrefixes@2019-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n sku: {\n name: 'Standard'\n }\n properties: {\n publicIPAddressVersion: 'IPv4'\n prefixLength: ${3:28}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-ip-prefix" + } + } + ] } }, { @@ -705,6 +1068,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:keyVaultSecret} 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = {\n name: ${2:'keyVaultName/name'}\n properties: {\n value: ${3:'value'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-keyvault-secret" + } + } + ] } }, { @@ -759,6 +1133,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:logAnalyticsSolution} 'Microsoft.OperationsManagement/solutions@2015-11-01-preview' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n workspaceResourceId: resourceId('Microsoft.OperationalInsights/workspaces', ${3:'logAnalyticsWorkspace'})\n containedResources: [\n resourceId('Microsoft.OperationalInsights/workspaces/views', ${3:'logAnalyticsWorkspace'}, ${4:'logAnalyticsSolution'})\n ]\n }\n plan: {\n name: ${5:'name'}\n product: ${6:'product'}\n publisher: ${7:'publisher'}\n promotionCode: ${8:'promotionCode'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-log-analytics-solution" + } + } + ] } }, { @@ -777,6 +1162,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:logAnalyticsWorkspace} 'Microsoft.OperationalInsights/workspaces@2020-10-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n sku: {\n name: ${3|'Free','Standard','Premium','Unlimited','PerNode','PerGB2018','Standalone'|}\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-log-analytics-workspace" + } + } + ] } }, { @@ -795,6 +1191,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:logicApp} 'Microsoft.Logic/integrationAccounts@2016-06-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n definition: {\n '$schema': 'https://schema.management.azure.com/schemas/2016-06-01/Microsoft.Logic.json'\n contentVersion: '1.0.0.0'\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-logic-app" + } + } + ] } }, { @@ -813,6 +1220,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:logicAppConnector} 'Microsoft.Web/connections@2015-08-01-preview' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n name: ${3:'name'}\n apiDefinitionUrl: subscriptionResourceId('Microsoft.Web/locations/managedApis', resourceGroup().location, ${4:'logicAppConnectorApi'})\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-logic-app-connector" + } + } + ] } }, { @@ -831,6 +1249,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:managedIdentity} 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {\n name: ${2:'name'}\n location: resourceGroup().location\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-managed-identity" + } + } + ] } }, { @@ -849,6 +1278,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:mediaServices} 'Microsoft.Media/mediaServices@2020-05-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n storageAccounts: [\n {\n id: resourceId('Microsoft.Storage/storageAccounts', ${3:'mediaServiceStorageAccount'})\n type: '${4|Primary,Secondary|}'\n }\n ]\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-media" + } + } + ] } }, { @@ -867,6 +1307,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:mySQLdb} 'Microsoft.DBforMySQL/servers@2017-12-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n administratorLogin: ${3:'administratorLogin'}\n administratorLoginPassword: ${4:'administratorLoginPassword'}\n createMode: ${5|'Default','GeoRestore','PointInTimeRestore','Replica'|}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-mysql" + } + } + ] } }, { @@ -885,6 +1336,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:networkInterface} 'Microsoft.Network/networkInterfaces@2020-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n ipConfigurations: [\n {\n name: ${3:'name'}\n properties: {\n privateIPAllocationMethod: '${4|Dynamic,Static|}'\n subnet: {\n id: resourceId('Microsoft.Network/virtualNetworks/subnets', ${5:'virtualNetwork'}, ${6:'subnet'})\n }\n }\n }\n ]\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-nic" + } + } + ] } }, { @@ -903,6 +1365,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:networkSecurityGroup} 'Microsoft.Network/networkSecurityGroups@2019-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n securityRules: [\n {\n name: ${3:'nsgRule'}\n properties: {\n description: ${4:'description'}\n protocol: ${5|'Tcp','Udp','*'|}\n sourcePortRange: ${6:'*'}\n destinationPortRange: ${7:'*'}\n sourceAddressPrefix: ${8:'*'}\n destinationAddressPrefix: ${9:'*'}\n access: ${10|'Allow','Deny'|}\n priority: ${11:100}\n direction: ${12|'Inbound','Outbound'|}\n }\n }\n ]\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-nsg" + } + } + ] } }, { @@ -921,6 +1394,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:networkSecurityGroupSecurityRule} 'Microsoft.Network/networkSecurityGroups/securityRules@2019-11-01' = {\n name: ${2:'networkSecurityGroup/name'}\n properties: {\n description: ${3:'description'}\n protocol: ${4|'*','Ah','Esp','Icmp','Tcp','Udb'|}\n sourcePortRange: ${5:'sourcePortRange'}\n destinationPortRange: ${6:'destinationPortRange'}\n sourceAddressPrefix: ${7:'sourceAddressPrefix'}\n destinationAddressPrefix: ${8:'destinationAddressPrefix'}\n access: ${9|'Allow','Deny'|}\n priority: ${10:100}\n direction: ${11|'Inbound','Outbound'|}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-nsgrule" + } + } + ] } }, { @@ -939,6 +1423,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:recoveryServiceVault} 'Microsoft.RecoveryServices/vaults@2021-01-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n sku: {\n name: ${3|'RS0','Standard'|}\n tier: ${4:'Standard'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-recovery-service-vault" + } + } + ] } }, { @@ -957,6 +1452,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:redisCache} 'Microsoft.Cache/Redis@2019-07-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n sku: {\n name: 'Basic'\n family: 'C'\n capacity: 0\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-redis" + } + } + ] } }, { @@ -975,6 +1481,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:routeTable} 'Microsoft.Network/routeTables@2019-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n routes: [\n {\n name: ${3:'name'}\n properties: {\n addressPrefix: ${4:'destinationCIDR'}\n nextHopType: ${5|'VirtualNetworkGateway','VnetLocal','Internet','VirtualAppliance','None'|}\n nextHopIpAddress: ${6:'nextHopIp'}\n }\n }\n ]\n disableBgpRoutePropagation: ${7|true,false|}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-route-table" + } + } + ] } }, { @@ -993,6 +1510,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:routeTableRoute} 'Microsoft.Network/routeTables/routes@2019-11-01' = {\n name: ${2:'routeTableName/name'}\n properties: {\n addressPrefix: ${3:'addressPrefix'}\n nextHopType: ${4|'VirtualNetworkGateway','VnetLocal','Internet','VirtualAppliance','None'|}\n nextHopIpAddress: ${5:'nextHopIp'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-route-table-route" + } + } + ] } }, { @@ -1011,6 +1539,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:sharedImageGallery} 'Microsoft.Compute/galleries@2020-09-30' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n description: ${3:'description'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-shared-image-gallery" + } + } + ] } }, { @@ -1029,6 +1568,17 @@ "textEdit": { "range": {}, "newText": "resource sqlServer 'Microsoft.Sql/servers@2014-04-01' ={\n name: ${1:'name'}\n location: resourceGroup().location\n}\n\nresource ${2:sqlServerDatabase} 'Microsoft.Sql/servers/databases@2014-04-01' = {\n parent: sqlServer\n name: ${3:'name'}\n location: resourceGroup().location\n properties: {\n collation: ${4:'collation'}\n edition: '${5|Basic,Business,BusinessCritical,DataWarehouse,Free,GeneralPurpose,Hyperscale,Premium,PremiumRS,Standard,Stretch,System,System2,Web|}'\n maxSizeBytes: ${6:'maxSizeBytes'}\n requestedServiceObjectiveName: '${7|Basic,DS100,DS1000,DS1200,DS1500,DS200,DS2000,DS300,DS400,DS500,DS600,DW100,DW1000,DW10000c,DW1000c,DW1200,DW1500,DW15000c,DW1500c,DW200,DW2000,DW2000c,DW2500c,DW300,DW3000,DW30000c,DW3000c,DW400,DW500,DW5000c,DW600,DW6000,DW6000c,DW7500c,ElasticPool,Free,P1,P11,P15,P2,P3,P4,P6,PRS1,PRS2,PRS4,PRS6,S0,S1,S12,S2,S3,S4,S6,S7,S9,System,System0,System1,System2,System2L,System3,System3L,System4,System4L|}'\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-sql" + } + } + ] } }, { @@ -1047,6 +1597,17 @@ "textEdit": { "range": {}, "newText": "resource sqlServerDatabase 'Microsoft.Sql/servers/databases@2014-04-01' = {\n name: ${1:'name'}\n location: ${2:'location'}\n}\n\nresource ${3:sqlDatabaseImport} 'Microsoft.Sql/servers/databases/extensions@2014-04-01' = {\n parent: sqlServerDatabase\n name: ${4:'name'}\n properties: {\n storageKeyType: '${5|StorageAccessKey,SharedAccessKey|}'\n storageKey: ${6:'storageKey'}\n storageUri: ${7:'storageUri'}\n administratorLogin: ${8:'administratorLogin'}\n administratorLoginPassword: ${9:'administratorLoginPassword'}\n operationMode: ${10:'operationMode'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-sql-db-import" + } + } + ] } }, { @@ -1065,6 +1626,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:storageaccount} 'Microsoft.Storage/storageAccounts@2021-02-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n kind: ${3|'StorageV2','Storage','BlobStorage','BlockBlobStorage','FileStorage'|}\n sku: {\n name: ${4:'Premium_LRS'}\n tier: ${5|'Premium','Standard'|}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-storage" + } + } + ] } }, { @@ -1083,6 +1655,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:templateSpec} 'Microsoft.Resources/templateSpecs@2019-06-01-preview' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n description: ${3:'description'}\n displayName: ${4:'displayName'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-template-spec" + } + } + ] } }, { @@ -1101,6 +1684,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:trafficManagerProfile} 'Microsoft.Network/trafficManagerProfiles@2018-04-01' = {\n name: ${2:'name'}\n location: 'global'\n properties: {\n profileStatus: 'Enabled'\n trafficRoutingMethod: ${3|'Performance','Priority','Weighted','Geographic'|}\n dnsConfig: {\n relativeName: ${4:'dnsConfigRelativeName'}\n ttl: 30\n }\n monitorConfig: {\n protocol: ${5|'HTTP','HTTPS','TCP'|}\n port: ${6:80}\n path: ${7:'path'}\n intervalInSeconds: ${8:30}\n timeoutInSeconds: ${9:5}\n toleratedNumberOfFailures: ${10:3}\n }\n endpoints: [\n {\n properties: {\n targetResourceId: ${11:'targetResourceId'}\n endpointStatus: ${12|'Enabled','Disabled'|}\n weight: ${13:100}\n priority: ${14:1}\n }\n }\n ]\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-traffic-manager" + } + } + ] } }, { @@ -1119,6 +1713,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:virtualWan} 'Microsoft.Network/virtualWans@2020-07-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n type: ${3|'Standard','Basic'|}\n disableVpnEncryption: false\n allowBranchToBranchTraffic: true\n allowVnetToVnetTraffic: true\n office365LocalBreakoutCategory: ${4|'Optimize','OptimizeAndAllow','All','None'|}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-virtual-wan" + } + } + ] } }, { @@ -1191,6 +1796,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:virtualNetwork} 'Microsoft.Network/virtualNetworks@2019-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n addressSpace: {\n addressPrefixes: [\n '10.0.0.0/16'\n ]\n }\n subnets: [\n {\n name: 'Subnet-1'\n properties: {\n addressPrefix: '10.0.0.0/24'\n }\n }\n {\n name: 'Subnet-2'\n properties: {\n addressPrefix: '10.0.1.0/24'\n }\n }\n ]\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-vnet" + } + } + ] } }, { @@ -1209,6 +1825,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:peering} 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2020-07-01' = {\n name: ${2:'virtualNetwork/name'}\n properties: {\n allowVirtualNetworkAccess: ${3|true,false|}\n allowForwardedTraffic: ${4|true,false|}\n allowGatewayTransit: ${5|true,false|}\n useRemoteGateways: ${6|true,false|}\n remoteVirtualNetwork: {\n id: resourceId('Microsoft.Network/virtualNetworks', ${7:'REQUIRED'})\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-vnet-peering" + } + } + ] } }, { @@ -1227,6 +1854,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:localNetworkGateway} 'Microsoft.Network/localNetworkGateways@2019-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n localNetworkAddressSpace: {\n addressPrefixes: [\n ${3:'REQUIRED'}\n ]\n }\n gatewayIpAddress: ${4:'gatewayIpAddress'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-vpn-local-gateway" + } + } + ] } }, { @@ -1317,6 +1955,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:applicationGroup} 'Microsoft.DesktopVirtualization/applicationgroups@2019-12-10-preview' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n friendlyName: ${3:'friendlyName'}\n applicationGroupType: ${4|'Desktop','RemoteApp'|}\n hostPoolArmPath: resourceId('Microsoft.DesktopVirtualization/hostpools', ${5:'REQUIRED'})\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-wvd-appgroup" + } + } + ] } }, { @@ -1335,6 +1984,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:hostPool} 'Microsoft.DesktopVirtualization/hostpools@2019-12-10-preview' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n friendlyName: ${3:'hostpoolFriendlyName'}\n hostPoolType: ${4|'Personal','Pooled'|}\n loadBalancerType: ${5|'BreadthFirst','DepthFirst','Persistent'|}\n preferredAppGroupType: ${6|'Desktop','RailApplications','None'|}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-wvd-hostpool" + } + } + ] } }, { @@ -1353,6 +2013,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:workSpace} 'Microsoft.DesktopVirtualization/workspaces@2019-12-10-preview' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n friendlyName: ${3:'friendlyName'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-wvd-workspace" + } + } + ] } }, { @@ -1385,6 +2056,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:Identifier} '${2:Provider/ParentType/ChildType@Version}' = {\n name: $3\n properties: {\n $0\n }\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "resource-child-defaults" + } + } + ] } }, { @@ -1403,6 +2085,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:Identifier} '${2:Provider/ParentType/ChildType@Version}' = {\n name: $3\n $0\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "resource-child-without-defaults" + } + } + ] } }, { @@ -1421,6 +2114,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:Identifier} '${2:Provider/Type@Version}' = {\n name: $3\n location: $4\n properties: {\n $0\n }\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "resource-defaults" + } + } + ] } }, { @@ -1439,6 +2143,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:Identifier} '${2:Provider/Type@Version}' = {\n name: $3\n $0\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "resource-without-defaults" + } + } + ] } }, { @@ -1485,6 +2200,17 @@ "textEdit": { "range": {}, "newText": "var ${1:Identifier} = $0" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "var" + } + } + ] } } ] \ No newline at end of file diff --git a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs index 7320b7f392f..d3b5b43f05e 100644 --- a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs @@ -41,7 +41,7 @@ public async Task ValidateDeclarationSnippetCompletionItemContainsCommandWithTel command?.Name.Should().Be(TelemetryConstants.CommandName); telemetryEvent?.EventName.Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); - telemetryEvent?.Properties?.ContainsKey("label"); + telemetryEvent?.Properties?.ContainsKey("name"); } } } diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index dce674e8994..f9ab1b8765f 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -87,7 +87,7 @@ private IEnumerable GetDeclarationCompletions(BicepCompletionCon { Dictionary properties = new Dictionary() { - { "label", resourceSnippet.Prefix } + { "name", resourceSnippet.Prefix } }; TelemetryEvent telemetryEvent = new(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion, properties); Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); diff --git a/src/vscode-bicep/.vscode/launch.json b/src/vscode-bicep/.vscode/launch.json index b0a31f07f31..86646cd1af6 100644 --- a/src/vscode-bicep/.vscode/launch.json +++ b/src/vscode-bicep/.vscode/launch.json @@ -2,7 +2,6 @@ { "version": "0.2.0", "configurations": [ - { "name": "Launch Extension", "type": "extensionHost", From a1bbe29221ebc88d93a1c306e9232a1509d5a4f5 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Fri, 21 May 2021 14:01:06 -0700 Subject: [PATCH 08/26] Fix issue in tests --- .../Files/Completions/declarations.json | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/src/Bicep.Core.Samples/Files/Completions/declarations.json b/src/Bicep.Core.Samples/Files/Completions/declarations.json index af9a21ddb15..6c6962fa8e1 100644 --- a/src/Bicep.Core.Samples/Files/Completions/declarations.json +++ b/src/Bicep.Core.Samples/Files/Completions/declarations.json @@ -260,6 +260,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:apiManagementInstance} 'Microsoft.ApiManagement/service@2020-12-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n sku:{\n capacity: ${3|0,1|}\n name: ${4|'Developer','Consumption'|}\n }\n properties:{\n virtualNetworkType: ${5:'None'}\n publisherEmail: ${6:'publisherEmail@contoso.com'}\n publisherName: ${7:'publisherName'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-api-management-instance" + } + } + ] } }, { @@ -974,6 +985,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:azureFunction} 'Microsoft.Web/sites@2020-12-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n kind: 'functionapp'\n properties: {\n serverFarmId: resourceId('Microsoft.Web/serverfarms', ${3:'serverFarmName'})\n siteConfig: {\n appSettings: [\n {\n name: 'AzureWebJobsDashboard'\n value: 'DefaultEndpointsProtocol=https;AccountName=${4:storageAccountName1};AccountKey=${listKeys(${5:'storageAccountID1'}, '2019-06-01').key1}'\n }\n {\n name: 'AzureWebJobsStorage'\n value: 'DefaultEndpointsProtocol=https;AccountName=${6:storageAccountName2};AccountKey=${listKeys(${7:'storageAccountID2'}, '2019-06-01').key1}'\n }\n {\n name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'\n value: 'DefaultEndpointsProtocol=https;AccountName=${8:storageAccountName3};AccountKey=${listKeys(${9:'storageAccountID3'}, '2019-06-01').key1}'\n }\n {\n name: 'WEBSITE_CONTENTSHARE'\n value: toLower(${2:'name'})\n }\n {\n name: 'FUNCTIONS_EXTENSION_VERSION'\n value: '~2'\n }\n {\n name: 'APPINSIGHTS_INSTRUMENTATIONKEY'\n value: reference(resourceId('microsoft.insights/components/', ${10:'applicationInsightsName'}), '2015-05-01').InstrumentationKey\n }\n {\n name: 'FUNCTIONS_WORKER_RUNTIME'\n value: '${11|dotnet,node,java|}'\n }\n ]\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-function" + } + } + ] } }, { @@ -1050,6 +1072,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:keyVault} 'Microsoft.KeyVault/vaults@2019-09-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n enabledForDeployment: true\n enabledForTemplateDeployment: true\n enabledForDiskEncryption: true\n tenantId: ${3:'tenantId'}\n accessPolicies: [\n {\n tenantId: ${3:'tenantId'}\n objectId: ${4:'objectId'}\n permissions: {\n keys: [\n 'get'\n ]\n secrets: [\n 'list'\n 'get'\n 'get'\n ]\n }\n }\n ]\n sku: {\n name: 'standard'\n family: 'A'\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-keyvault" + } + } + ] } }, { @@ -1097,6 +1130,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:loadBalancerExternal} 'Microsoft.Network/loadBalancers@2020-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n frontendIPConfigurations: [\n {\n name: ${3:'name'}\n properties: {\n publicIPAddress: {\n id: resourceId('Microsoft.Network/publicIPAddresses', ${4:'publicIP'})\n }\n }\n }\n ]\n backendAddressPools: [\n {\n name: ${5:'name'}\n }\n ]\n inboundNatRules: [\n {\n name: ${6:'name'}\n properties: {\n frontendIPConfiguration: {\n id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', ${2:'name'}, ${3:'name'})\n }\n protocol: '${7|Tcp,Udp,All|}'\n frontendPort: ${8:50001}\n backendPort: ${9:3389}\n enableFloatingIP: false\n }\n }\n ]\n loadBalancingRules: [\n {\n name: ${10:'name'}\n properties: {\n frontendIPConfiguration: {\n id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', ${2:'name'}, ${3:'name'})\n }\n backendAddressPool: {\n id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', ${2:'name'}, ${5:'name'})\n }\n protocol: '${11|Tcp,Udp,All|}'\n frontendPort: ${12:80}\n backendPort: ${13:80}\n enableFloatingIP: false\n idleTimeoutInMinutes: 5\n probe: {\n id: resourceId('Microsoft.Network/loadBalancers/probes', ${2:'name'}, ${14:'name'})\n }\n }\n }\n ]\n probes: [\n {\n name: ${14:'name'}\n properties: {\n protocol: '${15|Tcp,Udp,All|}'\n port: ${16:80}\n intervalInSeconds: 5\n numberOfProbes: 2\n }\n }\n ]\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-loadbalancer-external" + } + } + ] } }, { @@ -1115,6 +1159,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:loadBalancerInternal} 'Microsoft.Network/loadBalancers@2020-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n frontendIPConfigurations: [\n {\n name: ${3:'name'}\n properties: {\n privateIPAddress: ${4:'0.0.0.0'}\n privateIPAllocationMethod: 'Static'\n subnet: {\n id: resourceId('Microsoft.Network/virtualNetworks/subnets', ${5:'virtualNetwork'}, ${6:'subnet'})\n }\n }\n }\n ]\n backendAddressPools: [\n {\n name: ${7:'name'}\n }\n ]\n loadBalancingRules: [\n {\n name: ${8:'name'}\n properties: {\n frontendIPConfiguration: {\n id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', ${2:'name'}, ${3:'name'})\n }\n backendAddressPool: {\n id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', ${2:'name'}, ${7:'name'})\n }\n protocol: '${9|Tcp,Udp,All|}'\n frontendPort: ${10:80}\n backendPort: ${11:80}\n enableFloatingIP: false\n idleTimeoutInMinutes: 5\n probe: {\n id: resourceId('Microsoft.Network/loadBalancers/probes', ${2:'name'}, ${12:'name'})\n }\n }\n }\n ]\n probes: [\n {\n name: ${12:'name'}\n properties: {\n protocol: '${13|Tcp,Udp,All|}'\n port: ${14:80}\n intervalInSeconds: 5\n numberOfProbes: 2\n }\n }\n ]\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-loadbalancer-internal" + } + } + ] } }, { @@ -1742,6 +1797,17 @@ "textEdit": { "range": {}, "newText": "resource virtualMachine 'Microsoft.Compute/virtualMachines@2020-12-01' = {\n name: ${1:'name'}\n location: resourceGroup().location\n}\n\nresource ${2:windowsVMDsc} 'Microsoft.Compute/virtualMachines/extensions@2020-12-01' = {\n parent: virtualMachine\n name: ${3:'name'}\n location: resourceGroup().location\n properties: {\n publisher: 'Microsoft.Powershell'\n type: 'DSC'\n typeHandlerVersion: '2.9'\n autoUpgradeMinorVersion: true\n settings: {\n modulesUrl: ${4:'modulesUrl'}\n sasToken: ${5:'sasToken'}\n configurationFunction: ${6:'configurationFunction'}\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-vm-dsc" + } + } + ] } }, { @@ -1760,6 +1826,17 @@ "textEdit": { "range": {}, "newText": "resource virtualMachine 'Microsoft.Compute/virtualMachines@2020-12-01' = {\n name: ${1:'name'}\n location: resourceGroup().location\n}\n\nresource ${2:linuxVMExtensions} 'Microsoft.Compute/virtualMachines/extensions@2019-07-01' = {\n parent: virtualMachine\n name: ${3:'name'}\n location: resourceGroup().location\n properties: {\n publisher: 'Microsoft.Azure.Extensions'\n type: 'CustomScript'\n typeHandlerVersion: '2.1'\n autoUpgradeMinorVersion: true\n settings: {\n fileUris: [\n ${4:'fileUris'}\n ]\n }\n protectedSettings: {\n commandToExecute: 'sh ${5:customScript.sh}'\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-vm-script-linux" + } + } + ] } }, { @@ -1778,6 +1855,17 @@ "textEdit": { "range": {}, "newText": "resource virtualMachine 'Microsoft.Compute/virtualMachines@2020-12-01' = {\n name: ${1:'name'}\n location: resourceGroup().location\n}\n\nresource ${2:windowsVMExtensions} 'Microsoft.Compute/virtualMachines/extensions@2020-12-01' = {\n parent: virtualMachine\n name: ${3:'name'}\n location: resourceGroup().location\n properties: {\n publisher: 'Microsoft.Compute'\n type: 'CustomScriptExtension'\n typeHandlerVersion: '1.10'\n autoUpgradeMinorVersion: true\n settings: {\n fileUris: [\n ${4:'fileUris'}\n ]\n }\n protectedSettings: {\n commandToExecute: 'powershell -ExecutionPolicy Bypass -file ${5:customScript.ps1}'\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-vm-script-windows" + } + } + ] } }, { @@ -1883,6 +1971,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:vpnVnetConnection} 'Microsoft.Network/connections@2020-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n virtualNetworkGateway1: {\n id: resourceId('Microsoft.Network/virtualNetworkGateways', ${3:'vnetGateway'})\n properties:{}\n }\n localNetworkGateway2: {\n id: resourceId('Microsoft.Network/localNetworkGateways', ${4:'localGateway'})\n properties:{}\n }\n connectionType: '${5|IPsec,Vnet2Vnet,ExpressRoute,VPNClient|}'\n routingWeight: ${6:0}\n sharedKey: ${7:'sharedkey'}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-vpn-vnet-connection" + } + } + ] } }, { @@ -1901,6 +2000,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:virtualNetworkGateway} 'Microsoft.Network/virtualNetworkGateways@2020-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n properties: {\n ipConfigurations: [\n {\n name: ${3:'name'}\n properties: {\n privateIPAllocationMethod: 'Dynamic'\n subnet: {\n id: resourceId('Microsoft.Network/virtualNetworks/subnets', ${4:'virtualNetwork'}, ${5:'subnet'})\n }\n publicIPAddress: {\n id: resourceId('Microsoft.Network/publicIPAddresses', ${6:'publicIPAddress'})\n }\n }\n }\n ]\n sku: {\n name: '${7|Basic,HighPerformance,Standard,UltraPerformance,VpnGw1,VpnGw2,VpnGw3,VpnGw1AZ,VpnGw2AZ,VpnGw3AZ,ErGw1AZ,ErGw2AZ,ErGw3AZ|}'\n tier: '${8|Basic,HighPerformance,Standard,UltraPerformance,VpnGw1,VpnGw2,VpnGw3,VpnGw1AZ,VpnGw2AZ,VpnGw3AZ,ErGw1AZ,ErGw2AZ,ErGw3AZ|}'\n }\n gatewayType: '${9|Vpn,ExpressRoute|}'\n vpnType: '${10|PolicyBased,RouteBased|}'\n enableBgp: ${11|true,false|}\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-vpn-vnet-gateway" + } + } + ] } }, { @@ -1919,6 +2029,17 @@ "textEdit": { "range": {}, "newText": "resource ${1:webApplication} 'Microsoft.Web/sites@2018-11-01' = {\n name: ${2:'name'}\n location: resourceGroup().location\n tags: {\n 'hidden-related:${resourceGroup().id}/providers/Microsoft.Web/serverfarms/${3:'appServicePlan'}': 'Resource'\n }\n properties: {\n serverFarmId: resourceId('Microsoft.Web/serverfarms', ${4:'appServicePlan'})\n }\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-web-app" + } + } + ] } }, { @@ -1937,6 +2058,17 @@ "textEdit": { "range": {}, "newText": "resource webApplication 'Microsoft.Web/sites@2020-12-01' = {\n name: ${1:'name'}\n location: resourceGroup().location\n}\n\nresource ${2:'webApplicationExtension} 'Microsoft.Web/sites/extensions@2020-12-01' = {\n parent: webApplication\n name: 'MSDeploy'\n properties: {\n packageUri: ${3:'packageUri'}\n dbType: 'None'\n connectionString: ${4:'connectionString'}\n setParameters: {\n 'IIS Web Application Name': ${5:'name'}\n }\n }\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "EventName": "TopLevelDeclarationSnippetInsertion", + "Properties": { + "name": "res-web-app-deploy" + } + } + ] } }, { From 7a1227581b0f8954a208fb039a310bef384d3d12 Mon Sep 17 00:00:00 2001 From: Marcus Felling Date: Mon, 24 May 2021 09:03:02 -0500 Subject: [PATCH 09/26] Be explicit about VS Code telemetry --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 82e2abb0018..dc1036458bb 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ Because we are now treating the ARM Template as an IL, we expect and encourage o ## Telemetry -VS Code collects usage data and sends it to Microsoft to help improve our products and services. Read our [privacy statement](https://go.microsoft.com/fwlink/?LinkID=528096&clcid=0x409) to learn more. If you don’t wish to send usage data to Microsoft, you can set the `telemetry.enableTelemetry` setting to `false`. Learn more in our [FAQ](https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting). +When using the Bicep VS Code extension, VS Code collects usage data and sends it to Microsoft to help improve our products and services. Read our [privacy statement](https://go.microsoft.com/fwlink/?LinkID=528096&clcid=0x409) to learn more. If you don’t wish to send usage data to Microsoft, you can set the `telemetry.enableTelemetry` setting to `false`. Learn more in our [FAQ](https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting). ## Contributing See [Contributing to Bicep](./CONTRIBUTING.md) for information on building/running the code, contributing code, contributing examples and contributing feature requests or bug reports. From 468d9e120ce88537d634803eb8bdafeeef0d66fb Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Mon, 24 May 2021 10:08:58 -0700 Subject: [PATCH 10/26] Revert static removal --- src/Bicep.LangServer/Server.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bicep.LangServer/Server.cs b/src/Bicep.LangServer/Server.cs index 8d22296183b..d47ad4eee7e 100644 --- a/src/Bicep.LangServer/Server.cs +++ b/src/Bicep.LangServer/Server.cs @@ -89,7 +89,7 @@ public async Task RunAsync(CancellationToken cancellationToken) await server.WaitForExit; } - private void RegisterServices(CreationOptions creationOptions, IServiceCollection services) + private static void RegisterServices(CreationOptions creationOptions, IServiceCollection services) { // using type based registration so dependencies can be injected automatically // without manually constructing up the graph From 59a3a6c010b32c4cf1c8738c6b4e52b2e3690dfd Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Mon, 24 May 2021 10:37:45 -0700 Subject: [PATCH 11/26] Reverted and cleaned up some changes with withCommand(..) --- src/Bicep.LangServer/Completions/BicepCompletionProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index f9ab1b8765f..48073459d48 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -96,8 +96,8 @@ private IEnumerable GetDeclarationCompletions(BicepCompletionCon resourceSnippet.Detail, resourceSnippet.Text, context.ReplacementRange, - resourceSnippet.CompletionPriority, - command: command); + resourceSnippet.CompletionPriority) + .WithCommand(command); } } From b5e4f803b05073e47a3e700cd8458e3d26e3ae35 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Mon, 24 May 2021 10:51:19 -0700 Subject: [PATCH 12/26] Cleaned up integration tests --- .../TelemetryTests.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs index d3b5b43f05e..63c64198adb 100644 --- a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs @@ -11,6 +11,7 @@ using Bicep.LanguageServer.Telemetry; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json.Linq; using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -36,12 +37,18 @@ public async Task ValidateDeclarationSnippetCompletionItemContainsCommandWithTel }); CompletionItem completionItem = completions.Where(x => x.Kind == CompletionItemKind.Snippet && x.Label == "res-aks-cluster").First(); + Command? command = completionItem.Command; - TelemetryEvent? telemetryEvent = command?.Arguments?.First().ToObject(); + command.Should().NotBeNull(); + command!.Name.Should().Be(TelemetryConstants.CommandName); + + JArray? arguments = command!.Arguments; + arguments.Should().NotBeNull(); - command?.Name.Should().Be(TelemetryConstants.CommandName); - telemetryEvent?.EventName.Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); - telemetryEvent?.Properties?.ContainsKey("name"); + TelemetryEvent? telemetryEvent = arguments!.First().ToObject(); + telemetryEvent!.Should().NotBeNull(); + telemetryEvent!.EventName.Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); + telemetryEvent!.Properties?.ContainsKey("name"); } } } From fd986a67ceae641530a583af1385ee65ca7e8faf Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Mon, 24 May 2021 13:22:21 -0700 Subject: [PATCH 13/26] Addressed couple of CR comments --- .../TelemetryTests.cs | 12 ++++++------ src/Bicep.LangServer/Telemetry/TelemetryConstants.cs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs index 63c64198adb..64faf7fb839 100644 --- a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs @@ -39,16 +39,16 @@ public async Task ValidateDeclarationSnippetCompletionItemContainsCommandWithTel CompletionItem completionItem = completions.Where(x => x.Kind == CompletionItemKind.Snippet && x.Label == "res-aks-cluster").First(); Command? command = completionItem.Command; - command.Should().NotBeNull(); - command!.Name.Should().Be(TelemetryConstants.CommandName); + Assert.IsNotNull(command); + Assert.AreEqual(TelemetryConstants.CommandName, command!.Name); JArray? arguments = command!.Arguments; - arguments.Should().NotBeNull(); + Assert.IsNotNull(arguments); TelemetryEvent? telemetryEvent = arguments!.First().ToObject(); - telemetryEvent!.Should().NotBeNull(); - telemetryEvent!.EventName.Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); - telemetryEvent!.Properties?.ContainsKey("name"); + Assert.IsNotNull(telemetryEvent); + Assert.AreEqual(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion, telemetryEvent!.EventName); + Assert.IsTrue(telemetryEvent!.Properties?.ContainsKey("name")); } } } diff --git a/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs b/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs index a1600013fa5..30e52b131c8 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs +++ b/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs @@ -3,11 +3,11 @@ namespace Bicep.LanguageServer.Telemetry { - public class TelemetryConstants + public static class TelemetryConstants { public const string CommandName = "bicep.Telemetry"; - public class EventNames + public static class EventNames { public const string TopLevelDeclarationSnippetInsertion = nameof(TopLevelDeclarationSnippetInsertion); } From b9caa74c9cde6c0fa3cbbfd6b966ed50ca944d91 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Mon, 24 May 2021 14:23:56 -0700 Subject: [PATCH 14/26] Refactored TelemetryEvent creation --- .../Completions/BicepCompletionProvider.cs | 7 ++---- .../Telemetry/TelemetryEvent.cs | 23 +++++++++++++++---- .../Telemetry/TelemetryProvider.cs | 4 +++- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index 48073459d48..33b4a97e3aa 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -85,11 +85,8 @@ private IEnumerable GetDeclarationCompletions(BicepCompletionCon foreach (Snippet resourceSnippet in SnippetsProvider.GetTopLevelNamedDeclarationSnippets()) { - Dictionary properties = new Dictionary() - { - { "name", resourceSnippet.Prefix } - }; - TelemetryEvent telemetryEvent = new(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion, properties); + TelemetryEvent telemetryEvent = TelemetryEvent.Create(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); + telemetryEvent.Set("name", resourceSnippet.Prefix); Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); yield return CreateContextualSnippetCompletion(resourceSnippet.Prefix, diff --git a/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs b/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs index 5643ae65822..19ce088f650 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs +++ b/src/Bicep.LangServer/Telemetry/TelemetryEvent.cs @@ -8,13 +8,26 @@ namespace Bicep.LanguageServer.Telemetry { public class TelemetryEvent : TelemetryEventParams { - public string EventName { get; set; } - public Dictionary? Properties { get; set; } + public Dictionary Properties = new Dictionary(); - public TelemetryEvent(string eventName, Dictionary? properties) + public string? EventName { get; set; } + + public static TelemetryEvent Create(string eventName) + { + TelemetryEvent telemetryEvent = new TelemetryEvent(); + telemetryEvent.EventName = eventName; + return telemetryEvent; + } + } + + public static class TelemetryExtensions + { + public static void Set(this TelemetryEvent telemetryEvent, string name, string value) { - EventName = eventName; - Properties = properties; + if (!telemetryEvent.Properties.ContainsKey(name)) + { + telemetryEvent.Properties.Add(name, value); + } } } } diff --git a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs index 63467ef52e0..b622494732c 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs +++ b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs @@ -12,7 +12,9 @@ public class TelemetryProvider : ITelemetryProvider public void PostEvent(TelemetryEvent telemetryEvent) { - if (telemetryEvent is null || telemetryEvent.Properties?.Count == 0) + if (telemetryEvent is null || + !string.IsNullOrWhiteSpace(telemetryEvent.EventName) || + telemetryEvent.Properties?.Count == 0) { return; } From 186bb1386bc6b5a1169cdbd8ee87c573d39b3683 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Wed, 26 May 2021 12:46:35 -0700 Subject: [PATCH 15/26] Addressed some cr feedback --- .../BicepCompletionProviderTests.cs | 23 +++++++++------- .../Completions/BicepCompletionProvider.cs | 2 +- .../Handlers/BicepTelemetryHandler.cs | 27 +++++-------------- src/Bicep.LangServer/Program.cs | 4 +-- src/Bicep.LangServer/Server.cs | 9 +------ ...lemetryEvent.cs => BicepTelemetryEvent.cs} | 10 +++---- .../Telemetry/ITelemetryProvider.cs | 2 +- .../Telemetry/TelemetryProvider.cs | 13 ++++++--- 8 files changed, 37 insertions(+), 53 deletions(-) rename src/Bicep.LangServer/Telemetry/{TelemetryEvent.cs => BicepTelemetryEvent.cs} (60%) diff --git a/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs b/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs index ff83fa69792..c3734d17aee 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(); @@ -187,7 +190,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(); @@ -229,7 +232,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; @@ -246,7 +249,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; @@ -293,7 +296,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; @@ -351,7 +354,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 33b4a97e3aa..c1c1e594711 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 d47ad4eee7e..62605c22bdc 100644 --- a/src/Bicep.LangServer/Server.cs +++ b/src/Bicep.LangServer/Server.cs @@ -33,8 +33,6 @@ public class CreationOptions public IResourceTypeProvider? ResourceTypeProvider { get; set; } public IFileResolver? FileResolver { get; set; } - - public ITelemetryProvider? TelemetryProvider { get; set; } } private readonly OmnisharpLanguageServer server; @@ -75,11 +73,6 @@ private Server(CreationOptions creationOptions, Action on onOptionsFunc(options); }); - - if (creationOptions.TelemetryProvider is TelemetryProvider telemetryProvider) - { - telemetryProvider.LanguageServer = server; - } } public async Task RunAsync(CancellationToken cancellationToken) @@ -96,7 +89,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); } } } From 0f2b7650cdea8f1ffa98314bc0a34f7669fc262c Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Wed, 26 May 2021 13:06:23 -0700 Subject: [PATCH 16/26] Added comment in BicepTelemetryHandler --- src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs index 78c384960b9..e64b0f62217 100644 --- a/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs @@ -10,6 +10,12 @@ namespace Bicep.LanguageServer.Handlers { + // This handler is used to send a request from the client to the server(E.g. command sent as part + // of completion item of top level declaration snippet) which in turn triggers a request from server to client, + // asking the client to log a telemetry event + // Flow of events: + // 1. workspace/executeCommand request is sent from the client to the server + // 2. The above triggers telemetry/event from server to client public class BicepTelemetryHandler : ExecuteCommandHandlerBase { private readonly ITelemetryProvider TelemetryProvider; From a2b850e1dfe1df9efcb8f9bc9513e44b8588d507 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Wed, 26 May 2021 13:45:52 -0700 Subject: [PATCH 17/26] Posting telemetry events in couple other places --- .../Completions/BicepCompletionProvider.cs | 29 +++++++++++++++---- .../Telemetry/TelemetryConstants.cs | 2 ++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index c1c1e594711..2ce64027902 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -85,11 +85,13 @@ private IEnumerable GetDeclarationCompletions(BicepCompletionCon foreach (Snippet resourceSnippet in SnippetsProvider.GetTopLevelNamedDeclarationSnippets()) { + string prefix = resourceSnippet.Prefix; BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.Create(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); - telemetryEvent.Set("name", resourceSnippet.Prefix); + telemetryEvent.Set("name", prefix); + Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); - yield return CreateContextualSnippetCompletion(resourceSnippet.Prefix, + yield return CreateContextualSnippetCompletion(prefix, resourceSnippet.Detail, resourceSnippet.Text, context.ReplacementRange, @@ -404,12 +406,19 @@ private IEnumerable CreateResourceBodyCompletions(SemanticModel foreach (Snippet snippet in snippets) { - yield return CreateContextualSnippetCompletion(snippet!.Prefix, + string prefix = snippet.Prefix; + BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.Create(TelemetryConstants.EventNames.ResourceBodySnippetInsertion); + telemetryEvent.Set("name", prefix); + telemetryEvent.Set("type", typeSymbol.Name); + Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); + + yield return CreateContextualSnippetCompletion(prefix, snippet.Detail, snippet.Text, context.ReplacementRange, snippet.CompletionPriority, - preselect: true); + preselect: true) + .WithCommand(command); } } } @@ -423,12 +432,20 @@ private IEnumerable CreateModuleBodyCompletions(SemanticModel mo foreach (Snippet snippet in snippets) { - yield return CreateContextualSnippetCompletion(snippet!.Prefix, + string prefix = snippet.Prefix; + BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.Create(TelemetryConstants.EventNames.ResourceBodySnippetInsertion); + telemetryEvent.Set("name", prefix); + telemetryEvent.Set("type", typeSymbol.Name); + + Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); + + yield return CreateContextualSnippetCompletion(prefix, snippet.Detail, snippet.Text, context.ReplacementRange, snippet.CompletionPriority, - preselect: true); + preselect: true) + .WithCommand(command); } } } diff --git a/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs b/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs index 30e52b131c8..17dd84484e3 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs +++ b/src/Bicep.LangServer/Telemetry/TelemetryConstants.cs @@ -10,6 +10,8 @@ public static class TelemetryConstants public static class EventNames { public const string TopLevelDeclarationSnippetInsertion = nameof(TopLevelDeclarationSnippetInsertion); + public const string ResourceBodySnippetInsertion = nameof(ResourceBodySnippetInsertion); + public const string ModuleBodySnippetInsertion = nameof(ModuleBodySnippetInsertion); } } } From dae29aa72b0b8778202fe890bf05d3c6809388ec Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Wed, 26 May 2021 15:11:41 -0700 Subject: [PATCH 18/26] Updated integration test --- .../TelemetryTests.cs | 55 ++++++++++++++----- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs index 64faf7fb839..bae03445148 100644 --- a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs @@ -2,18 +2,22 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; -using Bicep.Core.Syntax; -using Bicep.Core.Text; +using Bicep.Core.FileSystem; using Bicep.Core.UnitTests.Assertions; +using Bicep.LangServer.IntegrationTests.Helpers; using Bicep.LanguageServer.Telemetry; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json.Linq; +using OmniSharp.Extensions.LanguageServer.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Window; +using OmniSharp.Extensions.LanguageServer.Protocol.Workspace; namespace Bicep.LangServer.IntegrationTests { @@ -25,30 +29,51 @@ public class TelemetryTests public TestContext? TestContext { get; set; } [TestMethod] - public async Task ValidateDeclarationSnippetCompletionItemContainsCommandWithTelemetryInformation() + public async Task VerifyTopLevelDeclarationSnippetInsertionFiresTelemetryEvent() { - var syntaxTree = SyntaxTree.Create(new Uri("file:///main.bicep"), string.Empty); - var client = await IntegrationTestHelper.StartServerWithTextAsync(string.Empty, syntaxTree.FileUri); + var fileSystemDict = new Dictionary(); + var telemetryReceived = new TaskCompletionSource(); + + var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync( + options => options.OnTelemetryEvent(telemetry => { + telemetryReceived.SetResult(telemetry); + }), + fileResolver: new InMemoryFileResolver(fileSystemDict)); + + var mainUri = DocumentUri.FromFileSystemPath("/main.bicep"); + fileSystemDict[mainUri.ToUri()] = string.Empty; + + client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(mainUri, fileSystemDict[mainUri.ToUri()], 1)); var completions = await client.RequestCompletion(new CompletionParams { - TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri), - Position = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, 0), + TextDocument = new TextDocumentIdentifier(mainUri), + Position = new Position(0, 0), }); CompletionItem completionItem = completions.Where(x => x.Kind == CompletionItemKind.Snippet && x.Label == "res-aks-cluster").First(); - Command? command = completionItem.Command; - Assert.IsNotNull(command); - Assert.AreEqual(TelemetryConstants.CommandName, command!.Name); + + command!.Name.Should().Be(TelemetryConstants.CommandName); JArray? arguments = command!.Arguments; - Assert.IsNotNull(arguments); + BicepTelemetryEvent? telemetryEvent = arguments!.First().ToObject(); + + telemetryEvent!.EventName.Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); + telemetryEvent!.Properties!.Should().ContainKey("name"); + + await client.ResolveCompletion(completionItem); + await client.Workspace.ExecuteCommand(command); + + TelemetryEventParams telemetryEventParams = await IntegrationTestHelper.WithTimeoutAsync(telemetryReceived.Task); - TelemetryEvent? telemetryEvent = arguments!.First().ToObject(); - Assert.IsNotNull(telemetryEvent); - Assert.AreEqual(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion, telemetryEvent!.EventName); - Assert.IsTrue(telemetryEvent!.Properties?.ContainsKey("name")); + telemetryEventParams.Data.Keys.Count.Should().Be(2); + telemetryEventParams.Data.Keys.Should().Contain("eventName"); + telemetryEventParams.Data.Keys.Should().Contain("properties"); + telemetryEventParams.Data["eventName"].ToString().Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); + telemetryEventParams.Data["properties"].ToString().Should().BeEquivalentToIgnoringNewlines(@"{ + ""name"": ""res-aks-cluster"" +}"); } } } From 6de1c0758b27eac065ef5f19b9537f4776747121 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Wed, 26 May 2021 19:53:46 -0700 Subject: [PATCH 19/26] Rebase from main --- .../Files/Completions/moduleObject.json | 11 ++++++ .../Files/Completions/resourceObject.json | 30 +++++++--------- .../Completions/moduleBodyCompletions.json | 22 ++++++++++++ .../Completions/objectPlusFor.json | 36 +++++++++++++++++++ .../TelemetryTests.cs | 18 +++++----- .../Completions/BicepCompletionProvider.cs | 28 ++++++++++----- .../Handlers/BicepTelemetryHandler.cs | 28 +++++++++++---- src/Bicep.LangServer/Server.cs | 3 -- .../Snippets/SnippetsProvider.cs | 10 +++--- .../Telemetry/BicepTelemetryEvent.cs | 2 +- 10 files changed, 139 insertions(+), 49 deletions(-) diff --git a/src/Bicep.Core.Samples/Files/Completions/moduleObject.json b/src/Bicep.Core.Samples/Files/Completions/moduleObject.json index 680e5679faf..dbe01eeef4c 100644 --- a/src/Bicep.Core.Samples/Files/Completions/moduleObject.json +++ b/src/Bicep.Core.Samples/Files/Completions/moduleObject.json @@ -83,6 +83,17 @@ "textEdit": { "range": {}, "newText": "{\n\t$0\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "Properties": { + "name": "{}" + }, + "EventName": "ModuleBodySnippetInsertion" + } + ] } } ] \ No newline at end of file diff --git a/src/Bicep.Core.Samples/Files/Completions/resourceObject.json b/src/Bicep.Core.Samples/Files/Completions/resourceObject.json index 8d02d88d705..9858a41e6a8 100644 --- a/src/Bicep.Core.Samples/Files/Completions/resourceObject.json +++ b/src/Bicep.Core.Samples/Files/Completions/resourceObject.json @@ -67,24 +67,6 @@ "newText": "if (${1:condition}) {\n\t$0\n}" } }, - { - "label": "snippet", - "kind": "snippet", - "detail": "Web Deploy for a Web Application", - "documentation": { - "kind": "markdown", - "value": "```bicep\n\n\n```" - }, - "deprecated": false, - "preselect": true, - "sortText": "2_snippet", - "insertTextFormat": "snippet", - "insertTextMode": "adjustIndentation", - "textEdit": { - "range": {}, - "newText": "\n" - } - }, { "label": "{}", "kind": "snippet", @@ -101,6 +83,18 @@ "textEdit": { "range": {}, "newText": "{\n\t$0\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "Properties": { + "name": "{}", + "type": "error" + }, + "EventName": "ResourceBodySnippetInsertion" + } + ] } } ] \ No newline at end of file diff --git a/src/Bicep.Core.Samples/Files/InvalidModules_LF/Completions/moduleBodyCompletions.json b/src/Bicep.Core.Samples/Files/InvalidModules_LF/Completions/moduleBodyCompletions.json index a3978150ccd..a4aeecab4d9 100644 --- a/src/Bicep.Core.Samples/Files/InvalidModules_LF/Completions/moduleBodyCompletions.json +++ b/src/Bicep.Core.Samples/Files/InvalidModules_LF/Completions/moduleBodyCompletions.json @@ -83,6 +83,17 @@ "textEdit": { "range": {}, "newText": "{\n\tname: $1\n\tparams: {\n\t\tarrayParam: $2\n\t\tobjParam: {\n\t\t}\n\t\tstringParamB: $3\n\t}\n\t$0\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "Properties": { + "name": "required-properties" + }, + "EventName": "ModuleBodySnippetInsertion" + } + ] } }, { @@ -101,6 +112,17 @@ "textEdit": { "range": {}, "newText": "{\n\t$0\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "Properties": { + "name": "{}" + }, + "EventName": "ModuleBodySnippetInsertion" + } + ] } } ] \ No newline at end of file diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/objectPlusFor.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/objectPlusFor.json index 320730150eb..a725d307d4a 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/objectPlusFor.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/objectPlusFor.json @@ -83,6 +83,18 @@ "textEdit": { "range": {}, "newText": "{\n\tname: $1\n\tlocation: $2\n\t$0\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "Properties": { + "name": "required-properties", + "type": "Microsoft.Network/dnsZones@2018-05-01" + }, + "EventName": "ResourceBodySnippetInsertion" + } + ] } }, { @@ -101,6 +113,18 @@ "textEdit": { "range": {}, "newText": "{\n name: ${2:'name'}\n location: 'global'\n}\n" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "Properties": { + "name": "snippet", + "type": "Microsoft.Network/dnsZones@2018-05-01" + }, + "EventName": "ResourceBodySnippetInsertion" + } + ] } }, { @@ -119,6 +143,18 @@ "textEdit": { "range": {}, "newText": "{\n\t$0\n}" + }, + "command": { + "command": "bicep.Telemetry", + "arguments": [ + { + "Properties": { + "name": "{}", + "type": "Microsoft.Network/dnsZones@2018-05-01" + }, + "EventName": "ResourceBodySnippetInsertion" + } + ] } } ] \ No newline at end of file diff --git a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs index bae03445148..fcc443f2cda 100644 --- a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs @@ -35,9 +35,11 @@ public async Task VerifyTopLevelDeclarationSnippetInsertionFiresTelemetryEvent() var telemetryReceived = new TaskCompletionSource(); var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync( - options => options.OnTelemetryEvent(telemetry => { - telemetryReceived.SetResult(telemetry); - }), + TestContext, + options => + { + options.OnTelemetryEvent(telemetry => telemetryReceived.SetResult(telemetry)); + }, fileResolver: new InMemoryFileResolver(fileSystemDict)); var mainUri = DocumentUri.FromFileSystemPath("/main.bicep"); @@ -67,11 +69,11 @@ public async Task VerifyTopLevelDeclarationSnippetInsertionFiresTelemetryEvent() TelemetryEventParams telemetryEventParams = await IntegrationTestHelper.WithTimeoutAsync(telemetryReceived.Task); - telemetryEventParams.Data.Keys.Count.Should().Be(2); - telemetryEventParams.Data.Keys.Should().Contain("eventName"); - telemetryEventParams.Data.Keys.Should().Contain("properties"); - telemetryEventParams.Data["eventName"].ToString().Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); - telemetryEventParams.Data["properties"].ToString().Should().BeEquivalentToIgnoringNewlines(@"{ + telemetryEventParams.ExtensionData.Keys.Count.Should().Be(2); + telemetryEventParams.ExtensionData.Keys.Should().Contain("eventName"); + telemetryEventParams.ExtensionData.Keys.Should().Contain("properties"); + telemetryEventParams.ExtensionData["eventName"].ToString().Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); + telemetryEventParams.ExtensionData["properties"].ToString().Should().BeEquivalentToIgnoringNewlines(@"{ ""name"": ""res-aks-cluster"" }"); } diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index 2ce64027902..ff0c4346980 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -95,8 +95,8 @@ private IEnumerable GetDeclarationCompletions(BicepCompletionCon resourceSnippet.Detail, resourceSnippet.Text, context.ReplacementRange, - resourceSnippet.CompletionPriority) - .WithCommand(command); + command, + resourceSnippet.CompletionPriority); } } @@ -416,9 +416,9 @@ private IEnumerable CreateResourceBodyCompletions(SemanticModel snippet.Detail, snippet.Text, context.ReplacementRange, + command, snippet.CompletionPriority, - preselect: true) - .WithCommand(command); + preselect: true); } } } @@ -433,9 +433,8 @@ private IEnumerable CreateModuleBodyCompletions(SemanticModel mo foreach (Snippet snippet in snippets) { string prefix = snippet.Prefix; - BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.Create(TelemetryConstants.EventNames.ResourceBodySnippetInsertion); + BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.Create(TelemetryConstants.EventNames.ModuleBodySnippetInsertion); telemetryEvent.Set("name", prefix); - telemetryEvent.Set("type", typeSymbol.Name); Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); @@ -443,9 +442,9 @@ private IEnumerable CreateModuleBodyCompletions(SemanticModel mo snippet.Detail, snippet.Text, context.ReplacementRange, + command, snippet.CompletionPriority, - preselect: true) - .WithCommand(command); + preselect: true); } } } @@ -998,6 +997,19 @@ private static CompletionItem CreateContextualSnippetCompletion(string label, st .Preselect(preselect) .Build(); + /// + /// Creates a completion with a contextual snippet with command option. This will look like a snippet to the user. + /// + private static CompletionItem CreateContextualSnippetCompletion(string label, string detail, string snippet, Range replacementRange, Command command, CompletionPriority priority = CompletionPriority.Medium, bool preselect = false) => + CompletionItemBuilder.Create(CompletionItemKind.Snippet, label) + .WithSnippetEdit(replacementRange, snippet) + .WithCommand(command) + .WithDetail(detail) + .WithDocumentation($"```bicep\n{new Snippet(snippet).FormatDocumentation()}\n```") + .WithSortText(GetSortText(label, priority)) + .Preselect(preselect) + .Build(); + /// /// Creates a completion with a contextual snippet. This will look like a snippet to the user. /// diff --git a/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs index e64b0f62217..e617ed5190f 100644 --- a/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs @@ -1,11 +1,13 @@ // 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 OmniSharp.Extensions.JsonRpc; +using Newtonsoft.Json.Linq; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Workspace; namespace Bicep.LanguageServer.Handlers @@ -20,16 +22,28 @@ public class BicepTelemetryHandler : ExecuteCommandHandlerBase Handle(BicepTelemetryEvent bicepTelemetryEvent, CancellationToken cancellationToken) + public override Task Handle(ExecuteCommandParams request, CancellationToken cancellationToken) { - TelemetryProvider.PostEvent(bicepTelemetryEvent); - return Unit.Task; + JArray? arguments = request.Arguments; + if (arguments is not null && arguments.Any() && + arguments[0] is JToken jToken && + jToken.ToObject() is BicepTelemetryEvent telemetryEvent) + { + TelemetryProvider.PostEvent(telemetryEvent); + return Task.FromResult(telemetryEvent); + } + + return Task.FromResult(BicepTelemetryEvent.Create(string.Empty)); } + + protected override ExecuteCommandRegistrationOptions CreateRegistrationOptions(ExecuteCommandCapability capability, ClientCapabilities clientCapabilities) => new() + { + Commands = new Container(TelemetryConstants.CommandName) + }; } } diff --git a/src/Bicep.LangServer/Server.cs b/src/Bicep.LangServer/Server.cs index 62605c22bdc..bfe68a86f38 100644 --- a/src/Bicep.LangServer/Server.cs +++ b/src/Bicep.LangServer/Server.cs @@ -17,8 +17,6 @@ using Bicep.LanguageServer.Snippets; using Bicep.LanguageServer.Telemetry; using Microsoft.Extensions.DependencyInjection; -using OmniSharp.Extensions.LanguageServer.Protocol.Client; -using OmniSharp.Extensions.LanguageServer.Protocol.Window; using OmniSharp.Extensions.LanguageServer.Server; using OmnisharpLanguageServer = OmniSharp.Extensions.LanguageServer.Server.LanguageServer; @@ -67,7 +65,6 @@ private Server(CreationOptions creationOptions, Action on .WithHandler() .WithHandler() .WithHandler() -#pragma warning restore 0612 .WithHandler() .WithServices(services => RegisterServices(creationOptions, services)); diff --git a/src/Bicep.LangServer/Snippets/SnippetsProvider.cs b/src/Bicep.LangServer/Snippets/SnippetsProvider.cs index 7bc4d8d90d5..9a12c5bec30 100644 --- a/src/Bicep.LangServer/Snippets/SnippetsProvider.cs +++ b/src/Bicep.LangServer/Snippets/SnippetsProvider.cs @@ -130,10 +130,12 @@ private void CacheResourceDeclarationAndDependencies(string template, string man if (declaredSymbol.DeclaringSyntax is ResourceDeclarationSyntax resourceDeclarationSyntax) { - string type = declaredSymbol.Type.Name; - - CacheResourceDeclaration(resourceDeclarationSyntax, type, template, description); - CacheResourceDependencies(kvp.Value, template, type); + if (declaredSymbol.Type is TypeSymbol typeSymbol && typeSymbol.TypeKind != TypeKind.Error) + { + string type = typeSymbol.Name; + CacheResourceDeclaration(resourceDeclarationSyntax, type, template, description); + CacheResourceDependencies(kvp.Value, template, type); + } } } } diff --git a/src/Bicep.LangServer/Telemetry/BicepTelemetryEvent.cs b/src/Bicep.LangServer/Telemetry/BicepTelemetryEvent.cs index 090466173e5..d9872f2d439 100644 --- a/src/Bicep.LangServer/Telemetry/BicepTelemetryEvent.cs +++ b/src/Bicep.LangServer/Telemetry/BicepTelemetryEvent.cs @@ -6,7 +6,7 @@ namespace Bicep.LanguageServer.Telemetry { - public class BicepTelemetryEvent : TelemetryEventParams + public record BicepTelemetryEvent : TelemetryEventParams { public Dictionary Properties { get; set; } = new Dictionary(); From 33e32ce8ed7d1ba67ad0aeea160515ad8560e61f Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Wed, 26 May 2021 23:30:57 -0700 Subject: [PATCH 20/26] Added integration tests for resource and module body snippet insertion --- .../TelemetryTests.cs | 91 ++++++++++++++++--- 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs index fcc443f2cda..c0ebd643526 100644 --- a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs @@ -30,30 +30,99 @@ public class TelemetryTests [TestMethod] public async Task VerifyTopLevelDeclarationSnippetInsertionFiresTelemetryEvent() + { + IDictionary properties = new Dictionary + { + { "name", "res-aks-cluster" } + }; + + TaskCompletionSource telemetryReceived = await ResolveCompletionAsync(string.Empty, "res-aks-cluster", TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion, properties); + TelemetryEventParams telemetryEventParams = await IntegrationTestHelper.WithTimeoutAsync(telemetryReceived.Task); + IDictionary extensionData = telemetryEventParams.ExtensionData; + ICollection keys = extensionData.Keys; + + keys.Count.Should().Be(2); + keys.Should().Contain("eventName"); + keys.Should().Contain("properties"); + extensionData["eventName"].ToString().Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); + extensionData["properties"].ToString().Should().BeEquivalentToIgnoringNewlines(@"{ + ""name"": ""res-aks-cluster"" +}"); + } + + [DataRow("required-properties", "{\r\n \"name\": \"required-properties\",\r\n \"type\": \"Microsoft.ContainerService/managedClusters@2021-03-01\"\r\n}")] + [DataRow("snippet", "{\r\n \"name\": \"snippet\",\r\n \"type\": \"Microsoft.ContainerService/managedClusters@2021-03-01\"\r\n}")] + [DataRow("{}", "{\r\n \"name\": \"{}\",\r\n \"type\": \"Microsoft.ContainerService/managedClusters@2021-03-01\"\r\n}")] + [DataTestMethod] + public async Task VerifyResourceBodySnippetInsertionFiresTelemetryEvent(string prefix, string expectedProperties) + { + string text = "resource aksCluster 'Microsoft.ContainerService/managedClusters@2021-03-01' = "; + IDictionary properties = new Dictionary + { + { "name", prefix }, + { "type", "Microsoft.ContainerService/managedClusters@2021-03-01" } + }; + + TaskCompletionSource telemetryReceived = await ResolveCompletionAsync(text, prefix, TelemetryConstants.EventNames.ResourceBodySnippetInsertion, properties); + TelemetryEventParams telemetryEventParams = await IntegrationTestHelper.WithTimeoutAsync(telemetryReceived.Task); + IDictionary extensionData = telemetryEventParams.ExtensionData; + ICollection keys = extensionData.Keys; + + keys.Count.Should().Be(2); + keys.Should().Contain("eventName"); + keys.Should().Contain("properties"); + extensionData["eventName"].ToString().Should().Be(TelemetryConstants.EventNames.ResourceBodySnippetInsertion); + extensionData["properties"].ToString().Should().BeEquivalentToIgnoringNewlines(expectedProperties); + } + + [TestMethod] + public async Task VerifyModuleBodySnippetInsertionFiresTelemetryEvent() + { + string text = "module foo 'test.bicep' = "; + IDictionary properties = new Dictionary + { + { "name", "{}" } + }; + + TaskCompletionSource telemetryReceived = await ResolveCompletionAsync(text, "{}", TelemetryConstants.EventNames.ModuleBodySnippetInsertion, properties); + TelemetryEventParams telemetryEventParams = await IntegrationTestHelper.WithTimeoutAsync(telemetryReceived.Task); + IDictionary extensionData = telemetryEventParams.ExtensionData; + ICollection keys = extensionData.Keys; + + keys.Count.Should().Be(2); + keys.Should().Contain("eventName"); + keys.Should().Contain("properties"); + extensionData["eventName"].ToString().Should().Be(TelemetryConstants.EventNames.ModuleBodySnippetInsertion); + extensionData["properties"].ToString().Should().BeEquivalentToIgnoringNewlines(@"{ + ""name"": ""{}"" +}"); + } + + private async Task> ResolveCompletionAsync(string text, string prefix, string eventName, IDictionary properties) { var fileSystemDict = new Dictionary(); var telemetryReceived = new TaskCompletionSource(); var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync( TestContext, - options => + options => { options.OnTelemetryEvent(telemetry => telemetryReceived.SetResult(telemetry)); }, fileResolver: new InMemoryFileResolver(fileSystemDict)); var mainUri = DocumentUri.FromFileSystemPath("/main.bicep"); - fileSystemDict[mainUri.ToUri()] = string.Empty; + fileSystemDict[mainUri.ToUri()] = text; client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(mainUri, fileSystemDict[mainUri.ToUri()], 1)); var completions = await client.RequestCompletion(new CompletionParams { TextDocument = new TextDocumentIdentifier(mainUri), - Position = new Position(0, 0), + Position = new Position(0, text.Length), }); - CompletionItem completionItem = completions.Where(x => x.Kind == CompletionItemKind.Snippet && x.Label == "res-aks-cluster").First(); + CompletionItem completionItem = completions.Where(x => x.Kind == CompletionItemKind.Snippet && x.Label == prefix).First(); Command? command = completionItem.Command; command!.Name.Should().Be(TelemetryConstants.CommandName); @@ -61,21 +130,13 @@ public async Task VerifyTopLevelDeclarationSnippetInsertionFiresTelemetryEvent() JArray? arguments = command!.Arguments; BicepTelemetryEvent? telemetryEvent = arguments!.First().ToObject(); - telemetryEvent!.EventName.Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); - telemetryEvent!.Properties!.Should().ContainKey("name"); + telemetryEvent!.EventName.Should().Be(eventName); + telemetryEvent!.Properties!.Should().Equal(properties); await client.ResolveCompletion(completionItem); await client.Workspace.ExecuteCommand(command); - TelemetryEventParams telemetryEventParams = await IntegrationTestHelper.WithTimeoutAsync(telemetryReceived.Task); - - telemetryEventParams.ExtensionData.Keys.Count.Should().Be(2); - telemetryEventParams.ExtensionData.Keys.Should().Contain("eventName"); - telemetryEventParams.ExtensionData.Keys.Should().Contain("properties"); - telemetryEventParams.ExtensionData["eventName"].ToString().Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); - telemetryEventParams.ExtensionData["properties"].ToString().Should().BeEquivalentToIgnoringNewlines(@"{ - ""name"": ""res-aks-cluster"" -}"); + return telemetryReceived; } } } From f9547a012329456da53093b7f42b72a2fd9f1044 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Wed, 26 May 2021 23:48:21 -0700 Subject: [PATCH 21/26] PostEvent should throw if invalid arguments are passed --- src/Bicep.LangServer/Telemetry/TelemetryProvider.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs index c793580956d..a6c2c080cbc 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs +++ b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using OmniSharp.Extensions.LanguageServer.Protocol.Server; using OmniSharp.Extensions.LanguageServer.Protocol.Window; @@ -17,11 +18,10 @@ public TelemetryProvider(ILanguageServerFacade server) public void PostEvent(BicepTelemetryEvent telemetryEvent) { - if (telemetryEvent is null || - string.IsNullOrWhiteSpace(telemetryEvent.EventName) || - telemetryEvent.Properties?.Count == 0) + if (string.IsNullOrWhiteSpace(telemetryEvent.EventName) || telemetryEvent.Properties.Count == 0) { - return; + throw new ArgumentException("Invalid telemetryEvent. Event name is either null, empty, consists only " + + "of white-space characters or no properties are set on the event"); } server.Window.SendTelemetryEvent(telemetryEvent); From 6036330a4a5d7fef59bd0fcc7873e765ef42dcc8 Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Thu, 27 May 2021 09:21:40 -0700 Subject: [PATCH 22/26] Revert changes to auto generated file- CoreResources.Designer.cs --- src/Bicep.Core/CoreResources.Designer.cs | 68 ++++++++++++------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Bicep.Core/CoreResources.Designer.cs b/src/Bicep.Core/CoreResources.Designer.cs index 20ae1e82cad..f5943be0fa2 100644 --- a/src/Bicep.Core/CoreResources.Designer.cs +++ b/src/Bicep.Core/CoreResources.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -10,8 +10,8 @@ namespace Bicep.Core { using System; - - + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -23,15 +23,15 @@ namespace Bicep.Core { [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class CoreResources { - + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal CoreResources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// @@ -45,7 +45,7 @@ internal CoreResources() { return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. @@ -59,16 +59,16 @@ internal CoreResources() { resourceCulture = value; } } - + /// - /// Looks up a localized string similar to Custom bicepconfig.json file found ({0}).. + /// Looks up a localized string similar to Custom bicepconfig.json file found [{0}].. /// internal static string BicepConfigCustomSettingsFoundFormatMessage { get { return ResourceManager.GetString("BicepConfigCustomSettingsFoundFormatMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to No bicepconfig.json found for configuration override.. /// @@ -77,99 +77,99 @@ internal static string BicepConfigNoCustomSettingsMessage { return ResourceManager.GetString("BicepConfigNoCustomSettingsMessage", resourceCulture); } } - + /// - /// Looks up a localized string similar to Environment URLs should not be hardcoded. Use the environment() function to ensure compatibility across clouds.. + /// Looks up a localized string similar to Environment URLs should not be hardcoded. Access URLs via the environment() function to keep references current.. /// internal static string EnvironmentUrlHardcodedRuleDescription { get { return ResourceManager.GetString("EnvironmentUrlHardcodedRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Use string interpolation instead of the concat function.. + /// Looks up a localized string similar to Dynamic variable should not use concat - string interpolation should be used.. /// internal static string InterpolateNotConcatRuleDescription { get { return ResourceManager.GetString("InterpolateNotConcatRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Linter is disabled in settings file located at {0} . + /// Looks up a localized string similar to Linter is disabled in settings file [{0}]. /// internal static string LinterDisabledFormatMessage { get { return ResourceManager.GetString("LinterDisabledFormatMessage", resourceCulture); } } - + /// - /// Looks up a localized string similar to Analyzer '{0}' encountered an unexpected exception. {1}. + /// Looks up a localized string similar to Analyzer '{0}' encountered an unexpected exception: {1} /// internal static string LinterRuleExceptionMessageFormat { get { return ResourceManager.GetString("LinterRuleExceptionMessageFormat", resourceCulture); } } - + /// - /// Looks up a localized string similar to Resource location should be specified by a parameter with no default value or one that defaults to 'global' or resourceGroup().location.. + /// Looks up a localized string similar to Best practice dictates that location be through a parameter that has no default or defaults to 'global' or resourceGroup().location.. /// internal static string LocationSetByParameterRuleDescription { get { return ResourceManager.GetString("LocationSetByParameterRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Parameter is declared but never used.. + /// Looks up a localized string similar to Declared parameter must be referenced within the document scope.. /// internal static string ParameterMustBeUsedRuleDescription { get { return ResourceManager.GetString("ParameterMustBeUsedRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Secure parameters should not have hardcoded defaults (except for empty or newGuid()).. + /// Looks up a localized string similar to Secure parameters can't have hardcoded default. This prevents storage of sensitive data in the Bicep declaration.. /// internal static string SecureParameterDefaultRuleDescription { get { return ResourceManager.GetString("SecureParameterDefaultRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to See {0}. + /// Looks up a localized string similar to [See : {0}]. /// internal static string SeeDocLinkFormat { get { return ResourceManager.GetString("SeeDocLinkFormat", resourceCulture); } } - + /// - /// Looks up a localized string similar to Remove unnecessary string interpolation.. + /// Looks up a localized string similar to String interpolation can be simplified. String variables can be directly assigned to string properties and variables.. /// internal static string SimplifyInterpolationRuleDescription { get { return ResourceManager.GetString("SimplifyInterpolationRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Remove unnecessary dependsOn.. + /// Looks up a localized string similar to Best Practice: remove unnecessary dependsOn.. /// internal static string UnnecessaryDependsOnRuleDescription { get { return ResourceManager.GetString("UnnecessaryDependsOnRuleDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Variable is declared but never used.. + /// Looks up a localized string similar to Declared variable encountered that is not used within scope.. /// internal static string UnusedVariableRuleDescription { get { @@ -177,4 +177,4 @@ internal static string UnusedVariableRuleDescription { } } } -} +} \ No newline at end of file From e877e00ba4a093b0592689e38ad0367b5ce5aabb Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Thu, 27 May 2021 09:26:57 -0700 Subject: [PATCH 23/26] Another attempt at reverting auto generated file --- src/Bicep.Core/CoreResources.Designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bicep.Core/CoreResources.Designer.cs b/src/Bicep.Core/CoreResources.Designer.cs index f5943be0fa2..0c9a3025f9e 100644 --- a/src/Bicep.Core/CoreResources.Designer.cs +++ b/src/Bicep.Core/CoreResources.Designer.cs @@ -177,4 +177,4 @@ internal static string UnusedVariableRuleDescription { } } } -} \ No newline at end of file +} From e62d29910cec9c57f076492d705211ce60115dae Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Thu, 27 May 2021 09:31:45 -0700 Subject: [PATCH 24/26] Reverted changes in ln 1 CoreResources.Designer.cs --- src/Bicep.Core/CoreResources.Designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bicep.Core/CoreResources.Designer.cs b/src/Bicep.Core/CoreResources.Designer.cs index 0c9a3025f9e..23cf5fb516c 100644 --- a/src/Bicep.Core/CoreResources.Designer.cs +++ b/src/Bicep.Core/CoreResources.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//----------------------------------------------------------------------------- // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 From 731f2994b28d5addae47b7de98f07b67af519b9a Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Thu, 27 May 2021 09:34:01 -0700 Subject: [PATCH 25/26] Another attempt at reverting auto generated file --- src/Bicep.Core/CoreResources.Designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bicep.Core/CoreResources.Designer.cs b/src/Bicep.Core/CoreResources.Designer.cs index 23cf5fb516c..0c9a3025f9e 100644 --- a/src/Bicep.Core/CoreResources.Designer.cs +++ b/src/Bicep.Core/CoreResources.Designer.cs @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 From 62d6763f06b3d53bb8189b517dab0567a37fd0bd Mon Sep 17 00:00:00 2001 From: Bhavya Subramanian Date: Thu, 27 May 2021 10:49:54 -0700 Subject: [PATCH 26/26] Fixed cr comments --- .../TelemetryTests.cs | 66 ++++++------------- .../Completions/BicepCompletionProvider.cs | 12 +--- .../Handlers/BicepTelemetryHandler.cs | 30 +++------ .../Telemetry/BicepTelemetryEvent.cs | 47 ++++++++----- .../Telemetry/TelemetryProvider.cs | 2 +- 5 files changed, 61 insertions(+), 96 deletions(-) diff --git a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs index c0ebd643526..9952f3af485 100644 --- a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs @@ -36,25 +36,17 @@ public async Task VerifyTopLevelDeclarationSnippetInsertionFiresTelemetryEvent() { "name", "res-aks-cluster" } }; - TaskCompletionSource telemetryReceived = await ResolveCompletionAsync(string.Empty, "res-aks-cluster", TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion, properties); - TelemetryEventParams telemetryEventParams = await IntegrationTestHelper.WithTimeoutAsync(telemetryReceived.Task); - IDictionary extensionData = telemetryEventParams.ExtensionData; - ICollection keys = extensionData.Keys; - - keys.Count.Should().Be(2); - keys.Should().Contain("eventName"); - keys.Should().Contain("properties"); - extensionData["eventName"].ToString().Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); - extensionData["properties"].ToString().Should().BeEquivalentToIgnoringNewlines(@"{ - ""name"": ""res-aks-cluster"" -}"); + BicepTelemetryEvent bicepTelemetryEvent = await ResolveCompletionAsync(string.Empty, "res-aks-cluster", TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion, properties); + + bicepTelemetryEvent.EventName.Should().Be(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); + bicepTelemetryEvent.Properties.Should().Equal(properties); } - [DataRow("required-properties", "{\r\n \"name\": \"required-properties\",\r\n \"type\": \"Microsoft.ContainerService/managedClusters@2021-03-01\"\r\n}")] - [DataRow("snippet", "{\r\n \"name\": \"snippet\",\r\n \"type\": \"Microsoft.ContainerService/managedClusters@2021-03-01\"\r\n}")] - [DataRow("{}", "{\r\n \"name\": \"{}\",\r\n \"type\": \"Microsoft.ContainerService/managedClusters@2021-03-01\"\r\n}")] + [DataRow("required-properties")] + [DataRow("snippet")] + [DataRow("{}")] [DataTestMethod] - public async Task VerifyResourceBodySnippetInsertionFiresTelemetryEvent(string prefix, string expectedProperties) + public async Task VerifyResourceBodySnippetInsertionFiresTelemetryEvent(string prefix) { string text = "resource aksCluster 'Microsoft.ContainerService/managedClusters@2021-03-01' = "; IDictionary properties = new Dictionary @@ -63,16 +55,10 @@ public async Task VerifyResourceBodySnippetInsertionFiresTelemetryEvent(string p { "type", "Microsoft.ContainerService/managedClusters@2021-03-01" } }; - TaskCompletionSource telemetryReceived = await ResolveCompletionAsync(text, prefix, TelemetryConstants.EventNames.ResourceBodySnippetInsertion, properties); - TelemetryEventParams telemetryEventParams = await IntegrationTestHelper.WithTimeoutAsync(telemetryReceived.Task); - IDictionary extensionData = telemetryEventParams.ExtensionData; - ICollection keys = extensionData.Keys; + BicepTelemetryEvent bicepTelemetryEvent = await ResolveCompletionAsync(text, prefix, TelemetryConstants.EventNames.ResourceBodySnippetInsertion, properties); - keys.Count.Should().Be(2); - keys.Should().Contain("eventName"); - keys.Should().Contain("properties"); - extensionData["eventName"].ToString().Should().Be(TelemetryConstants.EventNames.ResourceBodySnippetInsertion); - extensionData["properties"].ToString().Should().BeEquivalentToIgnoringNewlines(expectedProperties); + bicepTelemetryEvent.EventName.Should().Be(TelemetryConstants.EventNames.ResourceBodySnippetInsertion); + bicepTelemetryEvent.Properties.Should().Equal(properties); } [TestMethod] @@ -84,30 +70,22 @@ public async Task VerifyModuleBodySnippetInsertionFiresTelemetryEvent() { "name", "{}" } }; - TaskCompletionSource telemetryReceived = await ResolveCompletionAsync(text, "{}", TelemetryConstants.EventNames.ModuleBodySnippetInsertion, properties); - TelemetryEventParams telemetryEventParams = await IntegrationTestHelper.WithTimeoutAsync(telemetryReceived.Task); - IDictionary extensionData = telemetryEventParams.ExtensionData; - ICollection keys = extensionData.Keys; - - keys.Count.Should().Be(2); - keys.Should().Contain("eventName"); - keys.Should().Contain("properties"); - extensionData["eventName"].ToString().Should().Be(TelemetryConstants.EventNames.ModuleBodySnippetInsertion); - extensionData["properties"].ToString().Should().BeEquivalentToIgnoringNewlines(@"{ - ""name"": ""{}"" -}"); + BicepTelemetryEvent bicepTelemetryEvent = await ResolveCompletionAsync(text, "{}", TelemetryConstants.EventNames.ModuleBodySnippetInsertion, properties); + + bicepTelemetryEvent.EventName.Should().Be(TelemetryConstants.EventNames.ModuleBodySnippetInsertion); + bicepTelemetryEvent.Properties.Should().Equal(properties); } - private async Task> ResolveCompletionAsync(string text, string prefix, string eventName, IDictionary properties) + private async Task ResolveCompletionAsync(string text, string prefix, string eventName, IDictionary properties) { var fileSystemDict = new Dictionary(); - var telemetryReceived = new TaskCompletionSource(); + var telemetryReceived = new TaskCompletionSource(); var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync( TestContext, options => { - options.OnTelemetryEvent(telemetry => telemetryReceived.SetResult(telemetry)); + options.OnTelemetryEvent(telemetry => telemetryReceived.SetResult(telemetry)); }, fileResolver: new InMemoryFileResolver(fileSystemDict)); @@ -124,19 +102,13 @@ private async Task> ResolveCompletion CompletionItem completionItem = completions.Where(x => x.Kind == CompletionItemKind.Snippet && x.Label == prefix).First(); Command? command = completionItem.Command; - - command!.Name.Should().Be(TelemetryConstants.CommandName); - JArray? arguments = command!.Arguments; BicepTelemetryEvent? telemetryEvent = arguments!.First().ToObject(); - telemetryEvent!.EventName.Should().Be(eventName); - telemetryEvent!.Properties!.Should().Equal(properties); - await client.ResolveCompletion(completionItem); await client.Workspace.ExecuteCommand(command); - return telemetryReceived; + return await IntegrationTestHelper.WithTimeoutAsync(telemetryReceived.Task); } } } diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index ff0c4346980..1da2dcc97d0 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -86,9 +86,7 @@ private IEnumerable GetDeclarationCompletions(BicepCompletionCon foreach (Snippet resourceSnippet in SnippetsProvider.GetTopLevelNamedDeclarationSnippets()) { string prefix = resourceSnippet.Prefix; - BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.Create(TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion); - telemetryEvent.Set("name", prefix); - + BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.CreateTopLevelDeclarationSnippetInsertion(prefix); Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); yield return CreateContextualSnippetCompletion(prefix, @@ -407,9 +405,7 @@ private IEnumerable CreateResourceBodyCompletions(SemanticModel foreach (Snippet snippet in snippets) { string prefix = snippet.Prefix; - BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.Create(TelemetryConstants.EventNames.ResourceBodySnippetInsertion); - telemetryEvent.Set("name", prefix); - telemetryEvent.Set("type", typeSymbol.Name); + BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.CreateResourceBodySnippetInsertion(prefix, typeSymbol.Name); Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); yield return CreateContextualSnippetCompletion(prefix, @@ -433,9 +429,7 @@ private IEnumerable CreateModuleBodyCompletions(SemanticModel mo foreach (Snippet snippet in snippets) { string prefix = snippet.Prefix; - BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.Create(TelemetryConstants.EventNames.ModuleBodySnippetInsertion); - telemetryEvent.Set("name", prefix); - + BicepTelemetryEvent telemetryEvent = BicepTelemetryEvent.CreateModuleBodySnippetInsertion(prefix); Command command = Command.Create(TelemetryConstants.CommandName, telemetryEvent); yield return CreateContextualSnippetCompletion(prefix, diff --git a/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs index e617ed5190f..7d761c4fefb 100644 --- a/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepTelemetryHandler.cs @@ -1,13 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Linq; using System.Threading; using System.Threading.Tasks; using Bicep.LanguageServer.Telemetry; -using Newtonsoft.Json.Linq; -using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; -using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using MediatR; +using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Workspace; namespace Bicep.LanguageServer.Handlers @@ -18,32 +16,20 @@ namespace Bicep.LanguageServer.Handlers // Flow of events: // 1. workspace/executeCommand request is sent from the client to the server // 2. The above triggers telemetry/event from server to client - public class BicepTelemetryHandler : ExecuteCommandHandlerBase + public class BicepTelemetryHandler : ExecuteTypedCommandHandlerBase { private readonly ITelemetryProvider TelemetryProvider; - public BicepTelemetryHandler(ITelemetryProvider telemetryProvider) + public BicepTelemetryHandler(ITelemetryProvider telemetryProvider, ISerializer serializer) + : base(TelemetryConstants.CommandName, serializer) { TelemetryProvider = telemetryProvider; } - 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 BicepTelemetryEvent telemetryEvent) - { - TelemetryProvider.PostEvent(telemetryEvent); - return Task.FromResult(telemetryEvent); - } - - return Task.FromResult(BicepTelemetryEvent.Create(string.Empty)); + TelemetryProvider.PostEvent(bicepTelemetryEvent); + return Unit.Task; } - - protected override ExecuteCommandRegistrationOptions CreateRegistrationOptions(ExecuteCommandCapability capability, ClientCapabilities clientCapabilities) => new() - { - Commands = new Container(TelemetryConstants.CommandName) - }; } } diff --git a/src/Bicep.LangServer/Telemetry/BicepTelemetryEvent.cs b/src/Bicep.LangServer/Telemetry/BicepTelemetryEvent.cs index d9872f2d439..a875143293f 100644 --- a/src/Bicep.LangServer/Telemetry/BicepTelemetryEvent.cs +++ b/src/Bicep.LangServer/Telemetry/BicepTelemetryEvent.cs @@ -8,26 +8,39 @@ namespace Bicep.LanguageServer.Telemetry { public record BicepTelemetryEvent : TelemetryEventParams { - public Dictionary Properties { get; set; } = new Dictionary(); - public string? EventName { get; set; } - public static BicepTelemetryEvent Create(string eventName) - { - BicepTelemetryEvent telemetryEvent = new BicepTelemetryEvent(); - telemetryEvent.EventName = eventName; - return telemetryEvent; - } - } + public Dictionary? Properties { get; set; } - public static class TelemetryExtensions - { - public static void Set(this BicepTelemetryEvent telemetryEvent, string name, string value) - { - if (!telemetryEvent.Properties.ContainsKey(name)) + public static BicepTelemetryEvent CreateTopLevelDeclarationSnippetInsertion(string name) + => new BicepTelemetryEvent + { + EventName = TelemetryConstants.EventNames.TopLevelDeclarationSnippetInsertion, + Properties = new() + { + ["name"] = name, + }, + }; + + public static BicepTelemetryEvent CreateResourceBodySnippetInsertion(string name, string type) + => new BicepTelemetryEvent + { + EventName = TelemetryConstants.EventNames.ResourceBodySnippetInsertion, + Properties = new() + { + ["name"] = name, + ["type"] = type, + }, + }; + + public static BicepTelemetryEvent CreateModuleBodySnippetInsertion(string name) + => new BicepTelemetryEvent { - telemetryEvent.Properties.Add(name, value); - } - } + EventName = TelemetryConstants.EventNames.ModuleBodySnippetInsertion, + Properties = new() + { + ["name"] = name, + }, + }; } } diff --git a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs index a6c2c080cbc..294b80537b2 100644 --- a/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs +++ b/src/Bicep.LangServer/Telemetry/TelemetryProvider.cs @@ -18,7 +18,7 @@ public TelemetryProvider(ILanguageServerFacade server) public void PostEvent(BicepTelemetryEvent telemetryEvent) { - if (string.IsNullOrWhiteSpace(telemetryEvent.EventName) || telemetryEvent.Properties.Count == 0) + if (string.IsNullOrWhiteSpace(telemetryEvent.EventName) || telemetryEvent.Properties?.Count == 0) { throw new ArgumentException("Invalid telemetryEvent. Event name is either null, empty, consists only " + "of white-space characters or no properties are set on the event");