From caaa1be8bb68519ee0c514a387e313d80d7a929d Mon Sep 17 00:00:00 2001 From: Jonathan Eskew Date: Mon, 3 Oct 2022 18:00:07 -0400 Subject: [PATCH 1/6] Add support for sourcing feature flags from bicepconfig.json Also emit a deprecation notice when a feature flag is sourced from an environment variable. --- .../RootCommandTests.cs | 21 ++-- src/Bicep.Cli.IntegrationTests/TestBase.cs | 13 +-- src/Bicep.Cli/Commands/BuildCommand.cs | 15 ++- .../Commands/GenerateParametersFileCommand.cs | 13 ++- src/Bicep.Cli/Commands/RootCommand.cs | 43 +++---- .../Helpers/ServiceCollectionExtensions.cs | 9 +- src/Bicep.Cli/InvocationContext.cs | 9 +- src/Bicep.Cli/Program.cs | 7 +- src/Bicep.Cli/Services/CompilationService.cs | 15 ++- src/Bicep.Cli/Services/CompilationWriter.cs | 10 +- src/Bicep.Cli/Services/ParametersWriter.cs | 4 +- .../Services/PlaceholderParametersWriter.cs | 6 +- .../DecoratorTests.cs | 4 +- .../Emit/TemplateEmitterTests.cs | 12 +- .../ExamplesTests.cs | 4 +- .../ModuleTests.cs | 29 ++--- .../NestedResourceTests.cs | 26 ++--- .../RegistryTests.cs | 13 ++- .../ScenarioTests.cs | 2 +- .../TopLevelResourcePropertiesTests.cs | 20 ++-- .../Semantics/CompilationTests.cs | 2 +- .../Semantics/ParamsSemanticModelTests.cs | 4 +- .../Semantics/SemanticModelTests.cs | 10 +- .../TypeSystem/TypeValidationTests.cs | 4 +- src/Bicep.Core.Samples/DataSetsExtensions.cs | 14 ++- .../ApiVersion/ApiVersionProviderTests.cs | 2 +- .../BicepTestConstants.cs | 6 +- .../ConfigurationManagerTests.cs | 17 ++- .../UseRecentApiVersionRuleTests.cs | 2 + .../Emit/ExpressionConverterTests.cs | 2 +- .../Emit/InlineDependencyVisitorTests.cs | 6 +- .../Features/FeatureProviderTests.cs | 108 ++++++++++++++++++ .../Semantics/SymbolContextTests.cs | 2 +- .../AzureEventSourceListenerFactoryTests.cs | 9 +- .../Az/AzResourceTypeProviderTests.cs | 4 +- .../Utils/CompilationHelper.cs | 8 +- .../Utils/SourceFileGroupingFactory.cs | 2 +- .../Workspaces/BicepFileTests.cs | 6 +- .../ApiVersions/ApiVersionProviderFactory.cs | 25 ++++ .../ApiVersions/IApiVersionProviderFactory.cs | 25 ++++ .../Linter/Rules/UseRecentApiVersionRule.cs | 6 +- .../AnalyzersConfigurationExtensions.cs | 6 +- .../Configuration/ConfigurationManager.cs | 2 +- ...xperimentalFeaturesEnabledConfiguration.cs | 16 +++ .../Configuration/RootConfiguration.cs | 45 ++++++-- src/Bicep.Core/Configuration/bicepconfig.json | 3 +- .../BicepConfigFeatureProviderSource.cs | 23 ++++ .../Features/DefaultsFeatureProvider.cs | 25 ++++ ...vironmentVariablesFeatureProviderSource.cs | 34 ++++++ src/Bicep.Core/Features/FeatureProvider.cs | 77 ++++++++----- .../Features/FeatureProviderFactory.cs | 23 ++++ src/Bicep.Core/Features/IFeatureProvider.cs | 15 +++ .../Features/IFeatureProviderFactory.cs | 27 +++++ .../Features/IFeatureProviderSource.cs | 31 +++++ .../Registry/DefaultModuleRegistryProvider.cs | 7 +- src/Bicep.Core/Rewriters/RewriterHelper.cs | 2 +- src/Bicep.Core/Semantics/Compilation.cs | 14 +-- src/Bicep.Core/Semantics/SemanticModel.cs | 6 +- .../DecompilationTests.cs | 14 +-- src/Bicep.Decompiler/TemplateDecompiler.cs | 14 +-- .../BuildCommandTests.cs | 4 +- .../CodeActionTests.cs | 6 +- .../CompletionTests.cs | 5 +- .../GenerateParamsCommandTests.cs | 3 +- .../Helpers/IntegrationTestHelper.cs | 2 +- .../Helpers/LanguageServerHelper.cs | 4 +- .../ImportKubernetesManifestTests.cs | 3 +- .../ParamsCompletionTests.cs | 9 +- .../SemanticTokenTests.cs | 3 +- .../SnippetTemplatesTests.cs | 2 +- .../TelemetryTests.cs | 2 +- .../BicepCompilationManagerHelper.cs | 8 +- .../BicepCompilationManagerTests.cs | 4 +- .../BicepCompilationProviderTests.cs | 4 +- .../BicepCompletionProviderTests.cs | 18 +-- .../BicepCompletionContextTests.cs | 2 +- .../Handlers/BicepBuildCommandHandlerTests.cs | 20 ++-- ...BicepDeploymentScopeRequestHandlerTests.cs | 25 ++-- .../Snippets/SnippetsProviderTests.cs | 2 +- .../BicepParamsCompilationManager.cs | 15 ++- .../Handlers/BicepBuildCommandHandler.cs | 17 ++- .../Handlers/BicepCompletionHandler.cs | 8 +- .../BicepDeploymentScopeRequestHandler.cs | 20 ++-- .../BicepGenerateParamsCommandHandler.cs | 17 ++- .../BicepParamsCompletionHandler.cs | 8 +- .../BicepParamsDefinitionHandler.cs | 8 +- .../BicepParamsTextDocumentSyncHandler.cs | 18 +-- .../Providers/BicepCompilationProvider.cs | 12 +- src/Bicep.LangServer/Server.cs | 11 +- .../Snippets/SnippetsProvider.cs | 12 +- src/Bicep.Wasm/Interop.cs | 10 +- 91 files changed, 823 insertions(+), 382 deletions(-) create mode 100644 src/Bicep.Core.UnitTests/Features/FeatureProviderTests.cs create mode 100644 src/Bicep.Core/Analyzers/Linter/ApiVersions/ApiVersionProviderFactory.cs create mode 100644 src/Bicep.Core/Analyzers/Linter/ApiVersions/IApiVersionProviderFactory.cs create mode 100644 src/Bicep.Core/Configuration/ExperimentalFeaturesEnabledConfiguration.cs create mode 100644 src/Bicep.Core/Features/BicepConfigFeatureProviderSource.cs create mode 100644 src/Bicep.Core/Features/DefaultsFeatureProvider.cs create mode 100644 src/Bicep.Core/Features/EnvironmentVariablesFeatureProviderSource.cs create mode 100644 src/Bicep.Core/Features/FeatureProviderFactory.cs create mode 100644 src/Bicep.Core/Features/IFeatureProviderFactory.cs create mode 100644 src/Bicep.Core/Features/IFeatureProviderSource.cs diff --git a/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs b/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs index 13ebfbad773..0015110dd1d 100644 --- a/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs +++ b/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs @@ -1,12 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Bicep.Core.Features; using Bicep.Core.UnitTests.Assertions; +using Bicep.Core.UnitTests; using FluentAssertions; using FluentAssertions.Execution; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace Bicep.Cli.IntegrationTests @@ -14,6 +15,9 @@ namespace Bicep.Cli.IntegrationTests [TestClass] public class RootCommandTests : TestBase { + [NotNull] + public TestContext? TestContext { get; set; } + [TestMethod] public async Task Build_WithWrongArgs_ShouldFail_WithExpectedErrorMessage() { @@ -47,10 +51,7 @@ public async Task BicepVersionShouldPrintVersionInformation() [TestMethod] public async Task BicepHelpShouldPrintHelp() { - var featuresMock = Repository.Create(); - featuresMock.Setup(m => m.RegistryEnabled).Returns(true); - - var settings = CreateDefaultSettings() with { Features = featuresMock.Object }; + var settings = CreateDefaultSettings() with { Features = BicepTestConstants.CreateFeatureProvider(TestContext, registryEnabled: true) }; var (output, error, result) = await Bicep(settings, "--help"); @@ -130,10 +131,7 @@ public async Task BicepThirdPartyNoticesShouldPrintNotices() [TestMethod] public async Task BicepHelpShouldIncludePublishWhenRegistryEnabled() { - var featuresMock = Repository.Create(); - featuresMock.Setup(m => m.RegistryEnabled).Returns(true); - - var settings = CreateDefaultSettings() with { Features = featuresMock.Object }; + var settings = CreateDefaultSettings() with { Features = BicepTestConstants.CreateFeatureProvider(TestContext, registryEnabled: true) }; var (output, error, result) = await Bicep(settings, "--help"); @@ -154,10 +152,7 @@ public async Task BicepHelpShouldIncludePublishWhenRegistryEnabled() [TestMethod] public async Task BicepHelpShouldNotIncludePublishWhenRegistryDisabled() { - var featuresMock = Repository.Create(); - featuresMock.Setup(m => m.RegistryEnabled).Returns(false); - - var settings = CreateDefaultSettings() with { Features = featuresMock.Object }; + var settings = CreateDefaultSettings() with { Features = BicepTestConstants.CreateFeatureProvider(TestContext, registryEnabled: false) }; var (output, error, result) = await Bicep(settings, "--help"); diff --git a/src/Bicep.Cli.IntegrationTests/TestBase.cs b/src/Bicep.Cli.IntegrationTests/TestBase.cs index 4b218c42ec5..a07236306d2 100644 --- a/src/Bicep.Cli.IntegrationTests/TestBase.cs +++ b/src/Bicep.Cli.IntegrationTests/TestBase.cs @@ -6,7 +6,6 @@ using Bicep.Core.FileSystem; using Bicep.Core.Registry; using Bicep.Core.Semantics; -using Bicep.Core.Semantics.Namespaces; using Bicep.Core.Text; using Bicep.Core.UnitTests; using Bicep.Core.UnitTests.Utils; @@ -41,7 +40,7 @@ protected record InvocationSettings(IFeatureProvider Features, IContainerRegistr TestTypeHelper.CreateEmptyAzResourceTypeLoader(), @out, err, - features: settings.Features, + featureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(settings.Features), clientFactory: settings.ClientFactory, templateSpecRepositoryFactory: settings.TemplateSpecRepositoryFactory)).RunAsync(args)); @@ -55,9 +54,9 @@ protected static void AssertNoErrors(string error) protected static IEnumerable GetAllDiagnostics(string bicepFilePath, IContainerRegistryClientFactory clientFactory, ITemplateSpecRepositoryFactory templateSpecRepositoryFactory) { - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, BicepTestConstants.Features, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager); var sourceFileGrouping = SourceFileGroupingBuilder.Build(BicepTestConstants.FileResolver, dispatcher, new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath)); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var output = new List(); foreach (var (bicepFile, diagnostics) in compilation.GetAllDiagnosticsByBicepFile()) @@ -76,15 +75,15 @@ protected static IEnumerable GetAllDiagnostics(string bicepFilePath, ICo protected static IEnumerable GetAllParamDiagnostics(string paramFilePath, IContainerRegistryClientFactory clientFactory, ITemplateSpecRepositoryFactory templateSpecRepositoryFactory) { var configuration = BicepTestConstants.BuiltInConfiguration; - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, BicepTestConstants.Features, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager); var sourceFileGrouping = SourceFileGroupingBuilder.Build(BicepTestConstants.FileResolver, dispatcher, new Workspace(), PathHelper.FilePathToFileUrl(paramFilePath)); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var semanticModel = new ParamsSemanticModel(sourceFileGrouping, configuration, BicepTestConstants.Features, file => { var compilationGrouping = new SourceFileGrouping(BicepTestConstants.FileResolver, file.FileUri, sourceFileGrouping.FileResultByUri, sourceFileGrouping.UriResultByModule, sourceFileGrouping.SourceFileParentLookup); - return new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, compilationGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + return new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, compilationGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); }); var output = new List(); diff --git a/src/Bicep.Cli/Commands/BuildCommand.cs b/src/Bicep.Cli/Commands/BuildCommand.cs index de2704a1177..cd69ac6aefb 100644 --- a/src/Bicep.Cli/Commands/BuildCommand.cs +++ b/src/Bicep.Cli/Commands/BuildCommand.cs @@ -5,6 +5,8 @@ using Bicep.Cli.Arguments; using Bicep.Cli.Logging; using Bicep.Cli.Services; +using Bicep.Core.Emit; +using Bicep.Core.Features; using Bicep.Core.FileSystem; using Microsoft.Extensions.Logging; @@ -18,6 +20,7 @@ public class BuildCommand : ICommand private readonly CompilationService compilationService; private readonly CompilationWriter writer; private readonly ParametersWriter paramsWriter; + private readonly IFeatureProviderFactory featureProviderFactory; public BuildCommand( ILogger logger, @@ -25,7 +28,8 @@ public BuildCommand( InvocationContext invocationContext, CompilationService compilationService, CompilationWriter writer, - ParametersWriter paramsWriter) + ParametersWriter paramsWriter, + IFeatureProviderFactory featureProviderFactory) { this.logger = logger; this.diagnosticLogger = diagnosticLogger; @@ -33,18 +37,21 @@ public BuildCommand( this.compilationService = compilationService; this.writer = writer; this.paramsWriter = paramsWriter; + this.featureProviderFactory = featureProviderFactory; } public async Task RunAsync(BuildArguments args) { var inputPath = PathHelper.ResolvePath(args.InputFile); + var features = featureProviderFactory.GetFeatureProvider(PathHelper.FilePathToFileUrl(inputPath)); + var emitterSettings = new EmitterSettings(features); - if (invocationContext.EmitterSettings.EnableSymbolicNames) + if (emitterSettings.EnableSymbolicNames) { logger.LogWarning(CliResources.SymbolicNamesDisclaimerMessage); } - if (invocationContext.Features.ResourceTypedParamsAndOutputsEnabled) + if (features.ResourceTypedParamsAndOutputsEnabled) { logger.LogWarning(CliResources.ResourceTypesDisclaimerMessage); } @@ -72,7 +79,7 @@ public async Task RunAsync(BuildArguments args) // return non-zero exit code on errors return diagnosticLogger.ErrorCount > 0 ? 1 : 0; } - else if (invocationContext.Features.ParamsFilesEnabled && IsBicepparamsFile(inputPath)) + else if (features.ParamsFilesEnabled && IsBicepparamsFile(inputPath)) { var model = await compilationService.CompileParams(inputPath, args.NoRestore); diff --git a/src/Bicep.Cli/Commands/GenerateParametersFileCommand.cs b/src/Bicep.Cli/Commands/GenerateParametersFileCommand.cs index a4a9f9e0ac3..ccd599ea90c 100644 --- a/src/Bicep.Cli/Commands/GenerateParametersFileCommand.cs +++ b/src/Bicep.Cli/Commands/GenerateParametersFileCommand.cs @@ -4,6 +4,8 @@ using Bicep.Cli.Arguments; using Bicep.Cli.Logging; using Bicep.Cli.Services; +using Bicep.Core.Emit; +using Bicep.Core.Features; using Bicep.Core.FileSystem; using Microsoft.Extensions.Logging; using System.Threading.Tasks; @@ -17,31 +19,36 @@ public class GenerateParametersFileCommand : ICommand private readonly InvocationContext invocationContext; private readonly CompilationService compilationService; private readonly PlaceholderParametersWriter writer; + private readonly IFeatureProviderFactory featureProviderFactory; public GenerateParametersFileCommand( ILogger logger, IDiagnosticLogger diagnosticLogger, InvocationContext invocationContext, CompilationService compilationService, - PlaceholderParametersWriter writer) + PlaceholderParametersWriter writer, + IFeatureProviderFactory featureProviderFactory) { this.logger = logger; this.diagnosticLogger = diagnosticLogger; this.invocationContext = invocationContext; this.compilationService = compilationService; this.writer = writer; + this.featureProviderFactory = featureProviderFactory; } public async Task RunAsync(GenerateParametersFileArguments args) { var inputPath = PathHelper.ResolvePath(args.InputFile); + var features = featureProviderFactory.GetFeatureProvider(PathHelper.FilePathToFileUrl(inputPath)); + var emitterSettings = new EmitterSettings(features); - if (invocationContext.EmitterSettings.EnableSymbolicNames) + if (emitterSettings.EnableSymbolicNames) { logger.LogWarning(CliResources.SymbolicNamesDisclaimerMessage); } - if (invocationContext.Features.ResourceTypedParamsAndOutputsEnabled) + if (features.ResourceTypedParamsAndOutputsEnabled) { logger.LogWarning(CliResources.ResourceTypesDisclaimerMessage); } diff --git a/src/Bicep.Cli/Commands/RootCommand.cs b/src/Bicep.Cli/Commands/RootCommand.cs index 762a64b0998..3f30226e79f 100644 --- a/src/Bicep.Cli/Commands/RootCommand.cs +++ b/src/Bicep.Cli/Commands/RootCommand.cs @@ -52,29 +52,6 @@ private void PrintHelp() var exeName = ThisAssembly.AssemblyName; var versionString = GetVersionString(); - var registryText = -$@" - {exeName} publish --target - Publishes the .bicep file to the module registry. - - Arguments: - The input file (can be a Bicep file or an ARM template file) - The module reference - - Examples: - bicep publish file.bicep --target br:example.azurecr.io/hello/world:v1 - bicep publish file.json --target br:example.azurecr.io/hello/world:v1 - - {exeName} restore - Restores external modules from the specified Bicep file to the local module cache. - - Arguments: - The input file - -"; - - var registryPlaceholder = this.invocationContext.Features.RegistryEnabled ? registryText : Environment.NewLine; - var output = $@"Bicep CLI version {versionString} @@ -136,7 +113,25 @@ bicep generate-params file.bicep --stdout bicep generate-params file.bicep --outdir dir1 bicep generate-params file.bicep --outfile file.parameters.json -{registryPlaceholder} {exeName} [options] + + {exeName} publish --target + Publishes the .bicep file to the module registry. + + Arguments: + The input file (can be a Bicep file or an ARM template file) + The module reference + + Examples: + bicep publish file.bicep --target br:example.azurecr.io/hello/world:v1 + bicep publish file.json --target br:example.azurecr.io/hello/world:v1 + + {exeName} restore + Restores external modules from the specified Bicep file to the local module cache. + + Arguments: + The input file + + {exeName} [options] Options: --version -v Shows bicep version information --help -h Shows this usage information diff --git a/src/Bicep.Cli/Helpers/ServiceCollectionExtensions.cs b/src/Bicep.Cli/Helpers/ServiceCollectionExtensions.cs index 7b415f860ed..d34a36c0733 100644 --- a/src/Bicep.Cli/Helpers/ServiceCollectionExtensions.cs +++ b/src/Bicep.Cli/Helpers/ServiceCollectionExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using Bicep.Cli.Commands; +using Bicep.Core.Features; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; @@ -52,7 +53,13 @@ public static IServiceCollection AddInvocationContext(this IServiceCollection se // add contents of the context services.AddSingleton(context.NamespaceProvider); - services.AddSingleton(context.Features); + if (context.FeatureProviderFactory is {} factory) + { + services.AddSingleton(factory); + } else + { + services.AddSingleton(); + } services.AddSingleton(context.ClientFactory); services.AddSingleton(context.TemplateSpecRepositoryFactory); diff --git a/src/Bicep.Cli/InvocationContext.cs b/src/Bicep.Cli/InvocationContext.cs index 8932f1867d4..bfe5da25141 100644 --- a/src/Bicep.Cli/InvocationContext.cs +++ b/src/Bicep.Cli/InvocationContext.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Bicep.Core.Emit; using Bicep.Core.Features; using Bicep.Core.Registry; using Bicep.Core.Registry.Auth; @@ -17,14 +16,14 @@ public InvocationContext( IAzResourceTypeLoader azResourceTypeLoader, TextWriter outputWriter, TextWriter errorWriter, - IFeatureProvider? features = null, + IFeatureProviderFactory? featureProviderFactory = null, IContainerRegistryClientFactory? clientFactory = null, ITemplateSpecRepositoryFactory? templateSpecRepositoryFactory = null) { // keep the list of services in this class in sync with the logic in the AddInvocationContext() extension method OutputWriter = outputWriter; ErrorWriter = errorWriter; - Features = features ?? new FeatureProvider(); + FeatureProviderFactory = featureProviderFactory; ClientFactory = clientFactory ?? new ContainerRegistryClientFactory(new TokenCredentialFactory()); TemplateSpecRepositoryFactory = templateSpecRepositoryFactory ?? new TemplateSpecRepositoryFactory(new TokenCredentialFactory()); NamespaceProvider = new DefaultNamespaceProvider(azResourceTypeLoader); @@ -36,9 +35,7 @@ public InvocationContext( public TextWriter ErrorWriter { get; } - public EmitterSettings EmitterSettings => new EmitterSettings(Features); - - public IFeatureProvider Features { get; } + public IFeatureProviderFactory? FeatureProviderFactory { get; } public IContainerRegistryClientFactory ClientFactory { get; } diff --git a/src/Bicep.Cli/Program.cs b/src/Bicep.Cli/Program.cs index d4573a8a582..dd229d2e34e 100644 --- a/src/Bicep.Cli/Program.cs +++ b/src/Bicep.Cli/Program.cs @@ -46,19 +46,18 @@ public static async Task Main(string[] args) BicepDeploymentsInterop.Initialize(); - if (FeatureProvider.TracingEnabled) + if (IFeatureProvider.TracingEnabled) { Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); } // this event listener picks up SDK events and writes them to Trace.WriteLine() - using (FeatureProvider.TracingEnabled ? AzureEventSourceListenerFactory.Create(FeatureProvider.TracingVerbosity) : null) + using (IFeatureProvider.TracingEnabled ? AzureEventSourceListenerFactory.Create(IFeatureProvider.TracingVerbosity) : null) { var program = new Program(new InvocationContext( new AzResourceTypeLoader(), Console.Out, Console.Error, - features: null, clientFactory: null)); // this must be awaited so dispose of the listener occurs in the continuation @@ -127,7 +126,7 @@ private ServiceProvider ConfigureServices() .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() diff --git a/src/Bicep.Cli/Services/CompilationService.cs b/src/Bicep.Cli/Services/CompilationService.cs index be452cebdbf..c701a7d7323 100644 --- a/src/Bicep.Cli/Services/CompilationService.cs +++ b/src/Bicep.Cli/Services/CompilationService.cs @@ -31,7 +31,8 @@ public class CompilationService private readonly InvocationContext invocationContext; private readonly Workspace workspace; private readonly TemplateDecompiler decompiler; - private readonly IApiVersionProvider apiVersionProvider; + private readonly IApiVersionProviderFactory apiVersionProviderFactory; + private readonly IFeatureProviderFactory featureProviderFactory; public CompilationService( IDiagnosticLogger diagnosticLogger, @@ -40,7 +41,8 @@ public CompilationService( IModuleDispatcher moduleDispatcher, IConfigurationManager configurationManager, TemplateDecompiler decompiler, - IApiVersionProvider apiVersionProvider) + IApiVersionProviderFactory apiVersionProviderFactory, + IFeatureProviderFactory featureProviderFactory) { this.diagnosticLogger = diagnosticLogger; this.fileResolver = fileResolver; @@ -49,7 +51,8 @@ public CompilationService( this.invocationContext = invocationContext; this.workspace = new Workspace(); this.decompiler = decompiler; - this.apiVersionProvider = apiVersionProvider; + this.apiVersionProviderFactory = apiVersionProviderFactory; + this.featureProviderFactory = featureProviderFactory; } public async Task RestoreAsync(string inputPath, bool forceModulesRestore) @@ -97,7 +100,7 @@ public async Task CompileAsync(Uri inputUri, bool skipRestore) } } - var compilation = new Compilation(this.invocationContext.Features, this.invocationContext.NamespaceProvider, sourceFileGrouping, configurationManager, apiVersionProvider, new LinterAnalyzer()); + var compilation = new Compilation(featureProviderFactory, this.invocationContext.NamespaceProvider, sourceFileGrouping, configurationManager, apiVersionProviderFactory, new LinterAnalyzer()); LogDiagnostics(compilation); return compilation; @@ -121,11 +124,11 @@ public async Task CompileParams(string inputPath, bool skip } } - var model = new ParamsSemanticModel(sourceFileGrouping, configurationManager.GetConfiguration(inputUri), invocationContext.Features, file => { + var model = new ParamsSemanticModel(sourceFileGrouping, configurationManager.GetConfiguration(inputUri), featureProviderFactory.GetFeatureProvider(inputUri), file => { var compilationGrouping = new SourceFileGrouping(fileResolver, file.FileUri, sourceFileGrouping.FileResultByUri, sourceFileGrouping.UriResultByModule, sourceFileGrouping.SourceFileParentLookup); - return new Compilation(invocationContext.Features, this.invocationContext.NamespaceProvider, compilationGrouping, configurationManager, apiVersionProvider, new LinterAnalyzer()); + return new Compilation(featureProviderFactory, this.invocationContext.NamespaceProvider, compilationGrouping, configurationManager, apiVersionProviderFactory, new LinterAnalyzer()); }); LogParamDiagnostics(model); diff --git a/src/Bicep.Cli/Services/CompilationWriter.cs b/src/Bicep.Cli/Services/CompilationWriter.cs index 3736c44cc4b..efe41b774d8 100644 --- a/src/Bicep.Cli/Services/CompilationWriter.cs +++ b/src/Bicep.Cli/Services/CompilationWriter.cs @@ -24,25 +24,27 @@ public EmitResult ToFile(Compilation compilation, string outputPath) var fileStream = CreateFileStream(outputPath); using (fileStream) { - return new TemplateEmitter(compilation.GetEntrypointSemanticModel(), invocationContext.EmitterSettings).Emit(fileStream); + var semanticModel = compilation.GetEntrypointSemanticModel(); + return new TemplateEmitter(semanticModel, new EmitterSettings(semanticModel.Features)).Emit(fileStream); } } public EmitResult ToStream(Compilation compilation, Stream stream) { - return new TemplateEmitter(compilation.GetEntrypointSemanticModel(), invocationContext.EmitterSettings).Emit(stream); + var semanticModel = compilation.GetEntrypointSemanticModel(); + return new TemplateEmitter(semanticModel, new EmitterSettings(semanticModel.Features)).Emit(stream); } public EmitResult ToStdout(Compilation compilation) { var semanticModel = compilation.GetEntrypointSemanticModel(); - var sourceFileToTrack = this.invocationContext.Features.SourceMappingEnabled ? semanticModel.SourceFile : null; + var sourceFileToTrack = semanticModel.Features.SourceMappingEnabled ? semanticModel.SourceFile : null; using var writer = new SourceAwareJsonTextWriter(invocationContext.OutputWriter, sourceFileToTrack) { Formatting = Formatting.Indented }; - var emitter = new TemplateEmitter(semanticModel, invocationContext.EmitterSettings); + var emitter = new TemplateEmitter(semanticModel, new EmitterSettings(semanticModel.Features)); return emitter.Emit(writer); } diff --git a/src/Bicep.Cli/Services/ParametersWriter.cs b/src/Bicep.Cli/Services/ParametersWriter.cs index 4dc8b2360b1..302cc1ce4b2 100644 --- a/src/Bicep.Cli/Services/ParametersWriter.cs +++ b/src/Bicep.Cli/Services/ParametersWriter.cs @@ -25,7 +25,7 @@ public ParametersWriter(InvocationContext invocationContext) public EmitResult ToFile(ParamsSemanticModel paramSemanticModel, string outputPath) { using var fileStream = CreateFileStream(outputPath); - return new ParametersEmitter(paramSemanticModel, invocationContext.EmitterSettings).EmitParamsFile(fileStream); + return new ParametersEmitter(paramSemanticModel, new EmitterSettings(paramSemanticModel.Features)).EmitParamsFile(fileStream); } public EmitResult ToStdout(ParamsSemanticModel paramSemanticModel) @@ -35,7 +35,7 @@ public EmitResult ToStdout(ParamsSemanticModel paramSemanticModel) Formatting = Formatting.Indented }; - return new ParametersEmitter(paramSemanticModel, invocationContext.EmitterSettings).EmitParamsFile(writer); + return new ParametersEmitter(paramSemanticModel, new EmitterSettings(paramSemanticModel.Features)).EmitParamsFile(writer); } private static FileStream CreateFileStream(string path) diff --git a/src/Bicep.Cli/Services/PlaceholderParametersWriter.cs b/src/Bicep.Cli/Services/PlaceholderParametersWriter.cs index 3f0581cf4a6..418331d16d1 100644 --- a/src/Bicep.Cli/Services/PlaceholderParametersWriter.cs +++ b/src/Bicep.Cli/Services/PlaceholderParametersWriter.cs @@ -30,7 +30,8 @@ public EmitResult ToFile(Compilation compilation, string outputPath) existingContent = File.ReadAllText(outputPath); } using var fileStream = CreateFileStream(outputPath); - return new TemplateEmitter(compilation.GetEntrypointSemanticModel(), invocationContext.EmitterSettings).EmitParametersFile(fileStream, existingContent); + var semanticModel = compilation.GetEntrypointSemanticModel(); + return new TemplateEmitter(semanticModel, new EmitterSettings(semanticModel.Features)).EmitParametersFile(fileStream, existingContent); } public EmitResult ToStdout(Compilation compilation) @@ -40,7 +41,8 @@ public EmitResult ToStdout(Compilation compilation) Formatting = Formatting.Indented }; - return new TemplateEmitter(compilation.GetEntrypointSemanticModel(), invocationContext.EmitterSettings).EmitParametersFile(writer, string.Empty); + var semanticModel = compilation.GetEntrypointSemanticModel(); + return new TemplateEmitter(semanticModel, new EmitterSettings(semanticModel.Features)).EmitParametersFile(writer, string.Empty); } private static FileStream CreateFileStream(string path) diff --git a/src/Bicep.Core.IntegrationTests/DecoratorTests.cs b/src/Bicep.Core.IntegrationTests/DecoratorTests.cs index 92b32fa53dd..1891939ac49 100644 --- a/src/Bicep.Core.IntegrationTests/DecoratorTests.cs +++ b/src/Bicep.Core.IntegrationTests/DecoratorTests.cs @@ -78,7 +78,7 @@ param inputb string ", }; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnosticsByFile = compilation.GetAllDiagnosticsByBicepFile().ToDictionary(kvp => kvp.Key.FileUri, kvp => kvp.Value); var success = diagnosticsByFile.Values.SelectMany(x => x).All(d => d.Level != DiagnosticLevel.Error); @@ -192,7 +192,7 @@ param inputb string ", }; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnosticsByFile = compilation.GetAllDiagnosticsByBicepFile().ToDictionary(kvp => kvp.Key.FileUri, kvp => kvp.Value); var success = diagnosticsByFile.Values.SelectMany(x => x).All(d => d.Level != DiagnosticLevel.Error); diff --git a/src/Bicep.Core.IntegrationTests/Emit/TemplateEmitterTests.cs b/src/Bicep.Core.IntegrationTests/Emit/TemplateEmitterTests.cs index 795de8f2ded..c78cc2347aa 100644 --- a/src/Bicep.Core.IntegrationTests/Emit/TemplateEmitterTests.cs +++ b/src/Bicep.Core.IntegrationTests/Emit/TemplateEmitterTests.cs @@ -43,7 +43,7 @@ private async Task GetSourceFileGrouping(DataSet dataSet) var bicepFileUri = PathHelper.FilePathToFileUrl(bicepFilePath); // emitting the template should be successful - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, BicepTestConstants.CreateFeatureProvider(TestContext, registryEnabled: dataSet.HasExternalModules), BicepTestConstants.ConfigurationManager), BicepTestConstants.ConfigurationManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, IFeatureProviderFactory.WithStaticFeatureProvider(BicepTestConstants.CreateFeatureProvider(TestContext, registryEnabled: dataSet.HasExternalModules)), BicepTestConstants.ConfigurationManager), BicepTestConstants.ConfigurationManager); Workspace workspace = new(); var sourceFileGrouping = SourceFileGroupingBuilder.Build(BicepTestConstants.FileResolver, dispatcher, workspace, PathHelper.FilePathToFileUrl(bicepFilePath)); if (await dispatcher.RestoreModules(dispatcher.GetValidModuleReferences(sourceFileGrouping.GetModulesToRestore()))) @@ -268,11 +268,11 @@ public void TemplateEmitter_should_not_dispose_text_writer() private EmitResult EmitTemplate(SourceFileGrouping sourceFileGrouping, IFeatureProvider features, string filePath) { var compilation = new Compilation( - features, + IFeatureProviderFactory.WithStaticFeatureProvider(features), TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, - BicepTestConstants.ApiVersionProvider, + BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), new(features)); @@ -283,11 +283,11 @@ private EmitResult EmitTemplate(SourceFileGrouping sourceFileGrouping, IFeatureP private EmitResult EmitTemplate(SourceFileGrouping sourceFileGrouping, IFeatureProvider features, MemoryStream memoryStream) { var compilation = new Compilation( - features, + IFeatureProviderFactory.WithStaticFeatureProvider(features), TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, - BicepTestConstants.ApiVersionProvider, + BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), new(features)); @@ -301,7 +301,7 @@ private EmitResult EmitParam(SourceFileGrouping sourceFileGrouping, IFeatureProv var compilationGrouping = new SourceFileGrouping(BicepTestConstants.FileResolver, file.FileUri, sourceFileGrouping.FileResultByUri, sourceFileGrouping.UriResultByModule, sourceFileGrouping.SourceFileParentLookup); - return new Compilation(features, TestTypeHelper.CreateEmptyProvider(), compilationGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + return new Compilation(IFeatureProviderFactory.WithStaticFeatureProvider(features), TestTypeHelper.CreateEmptyProvider(), compilationGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); }); var emitter = new ParametersEmitter(model, new(features)); diff --git a/src/Bicep.Core.IntegrationTests/ExamplesTests.cs b/src/Bicep.Core.IntegrationTests/ExamplesTests.cs index fbf01c8ed8d..4051cccb9ca 100644 --- a/src/Bicep.Core.IntegrationTests/ExamplesTests.cs +++ b/src/Bicep.Core.IntegrationTests/ExamplesTests.cs @@ -44,11 +44,11 @@ private void RunExampleTest(EmbeddedFile embeddedBicep, IFeatureProvider feature var dispatcher = new ModuleDispatcher(BicepTestConstants.RegistryProvider, configManager); var sourceFileGrouping = SourceFileGroupingBuilder.Build(BicepTestConstants.FileResolver, dispatcher, new Workspace(), PathHelper.FilePathToFileUrl(bicepFile.OutputFilePath)); var compilation = new Compilation( - features, + IFeatureProviderFactory.WithStaticFeatureProvider(features), new DefaultNamespaceProvider(BicepTestConstants.AzResourceTypeLoader), sourceFileGrouping, configManager, - BicepTestConstants.ApiVersionProvider, + BicepTestConstants.ApiVersionProviderFactory, new LinterAnalyzer()); var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), new EmitterSettings(features)); diff --git a/src/Bicep.Core.IntegrationTests/ModuleTests.cs b/src/Bicep.Core.IntegrationTests/ModuleTests.cs index b1699f2a458..66802bebf35 100644 --- a/src/Bicep.Core.IntegrationTests/ModuleTests.cs +++ b/src/Bicep.Core.IntegrationTests/ModuleTests.cs @@ -87,7 +87,7 @@ param inputb string ", }; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProvider, LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); var (success, diagnosticsByFile) = GetSuccessAndDiagnosticsByFile(compilation); diagnosticsByFile.Values.SelectMany(x => x).Should().BeEmpty(); @@ -116,7 +116,7 @@ param inputb string ", }; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProvider, LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); var (success, diagnosticsByFile) = GetSuccessAndDiagnosticsByFile(compilation); diagnosticsByFile[mainUri].Should().HaveDiagnostics(new[] { @@ -170,7 +170,7 @@ param inputb string ", }; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProvider, LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); var (success, diagnosticsByFile) = GetSuccessAndDiagnosticsByFile(compilation); diagnosticsByFile[mainUri].Should().HaveDiagnostics(new[] { @@ -235,9 +235,10 @@ param inputb string SetupFileReaderMock(mockFileResolver, mainFileUri, mainFileContents, null); mockFileResolver.Setup(x => x.TryResolveFilePath(mainFileUri, "modulea.bicep")).Returns((Uri?)null); - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(mockFileResolver.Object, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.Features, ConfigurationManager), ConfigurationManager); + var featureManager = BicepTestConstants.FeatureProviderFactory; + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(mockFileResolver.Object, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, featureManager, ConfigurationManager), ConfigurationManager); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingBuilder.Build(mockFileResolver.Object, dispatcher, new Workspace(), mainFileUri), ConfigurationManager, BicepTestConstants.ApiVersionProvider, LinterAnalyzer); + var compilation = new Compilation(featureManager, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingBuilder.Build(mockFileResolver.Object, dispatcher, new Workspace(), mainFileUri), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); var (success, diagnosticsByFile) = GetSuccessAndDiagnosticsByFile(compilation); diagnosticsByFile[mainFileUri].Should().HaveDiagnostics(new[] { @@ -309,7 +310,7 @@ param inputb int " }; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProvider, LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); var (success, diagnosticsByFile) = GetSuccessAndDiagnosticsByFile(compilation); diagnosticsByFile.Values.SelectMany(x => x).Should().BeEmpty(); @@ -333,26 +334,26 @@ param inputb int // Confirming hashes equal individual template hashes ModuleTemplateHashValidator( - new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(new Dictionary + new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(new Dictionary { [moduleAUri] = files[moduleAUri] }, - moduleAUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProvider, LinterAnalyzer), moduleATemplateHash); + moduleAUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer), moduleATemplateHash); ModuleTemplateHashValidator( - new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(new Dictionary + new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(new Dictionary { [moduleBUri] = files[moduleBUri], [moduleCUri] = files[moduleCUri] }, - moduleBUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProvider, LinterAnalyzer), moduleBTemplateHash); + moduleBUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer), moduleBTemplateHash); ModuleTemplateHashValidator( - new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(new Dictionary + new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(new Dictionary { [moduleCUri] = files[moduleCUri] }, - moduleCUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProvider, LinterAnalyzer), moduleCTemplateHash); + moduleCUri, BicepTestConstants.FileResolver, ConfigurationManager), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer), moduleCTemplateHash); } [TestMethod] @@ -378,10 +379,10 @@ param inputb string SetupFileReaderMock(mockFileResolver, moduleAUri, null, x => x.ErrorOccurredReadingFile("Mock read failure!")); mockFileResolver.Setup(x => x.TryResolveFilePath(mainUri, "modulea.bicep")).Returns(moduleAUri); - var featureManager = BicepTestConstants.Features; + var featureManager = BicepTestConstants.FeatureProviderFactory; var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(mockFileResolver.Object, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, featureManager, ConfigurationManager), ConfigurationManager); - var compilation = new Compilation(featureManager, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingBuilder.Build(mockFileResolver.Object, dispatcher, new Workspace(), mainUri), ConfigurationManager, BicepTestConstants.ApiVersionProvider, LinterAnalyzer); + var compilation = new Compilation(featureManager, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingBuilder.Build(mockFileResolver.Object, dispatcher, new Workspace(), mainUri), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); var (success, diagnosticsByFile) = GetSuccessAndDiagnosticsByFile(compilation); diagnosticsByFile[mainUri].Should().HaveDiagnostics(new[] { diff --git a/src/Bicep.Core.IntegrationTests/NestedResourceTests.cs b/src/Bicep.Core.IntegrationTests/NestedResourceTests.cs index 9de7fbde830..7eb6988b811 100644 --- a/src/Bicep.Core.IntegrationTests/NestedResourceTests.cs +++ b/src/Bicep.Core.IntegrationTests/NestedResourceTests.cs @@ -48,7 +48,7 @@ public void NestedResources_symbols_are_bound() } "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var model = compilation.GetEntrypointSemanticModel(); model.GetAllDiagnostics().ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().BeEmpty(); @@ -86,7 +86,7 @@ public void NestedResources_resource_can_contain_property_called_resource() } "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var model = compilation.GetEntrypointSemanticModel(); // The property "resource" is not allowed ... @@ -144,7 +144,7 @@ public void NestedResources_valid_resource_references() output fromGrandchild string = parent::child::grandchild.properties.style "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var model = compilation.GetEntrypointSemanticModel(); model.GetAllDiagnostics().ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().BeEmpty(); @@ -204,7 +204,7 @@ public void NestedResources_invalid_resource_references() output fromGrandchildInvalid string = parent::child::cousin.properties.temperature "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var model = compilation.GetEntrypointSemanticModel(); model.GetAllDiagnostics().ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().HaveDiagnostics(new[]{ @@ -239,7 +239,7 @@ public void NestedResources_child_cannot_be_referenced_outside_of_scope() } "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics(); diagnostics.ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().HaveDiagnostics(new[] { ("BCP057", DiagnosticLevel.Error, "The name \"child\" does not exist in the current context."), @@ -264,7 +264,7 @@ public void NestedResources_child_cannot_specify_qualified_type() } "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics(); diagnostics.ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().HaveDiagnostics(new[] { ("BCP156", DiagnosticLevel.Error, "The resource type segment \"My.RP/parentType/childType@2020-01-01\" is invalid. Nested resources must specify a single type segment, and optionally can specify an api version using the format \"@\"."), @@ -289,7 +289,7 @@ public void NestedResources_error_in_base_type() } "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics(); diagnostics.ExcludingLinterDiagnostics().Should().HaveDiagnostics(new[] { ("BCP029", DiagnosticLevel.Error, "The resource type is not valid. Specify a valid resource type of format \"@\"."), @@ -321,7 +321,7 @@ public void NestedResources_error_in_parent_type() } "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics(); diagnostics.ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().HaveDiagnostics(new[] { ("BCP156", DiagnosticLevel.Error, "The resource type segment \"My.RP/parentType/childType@2020-01-01\" is invalid. Nested resources must specify a single type segment, and optionally can specify an api version using the format \"@\"."), @@ -348,7 +348,7 @@ public void NestedResources_child_cycle_is_detected_correctly() } "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); compilation.GetEntrypointSemanticModel().GetAllDiagnostics().ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().HaveDiagnostics(new[] { ("BCP080", DiagnosticLevel.Error, "The expression is involved in a cycle (\"child\" -> \"parent\")."), }); @@ -379,7 +379,7 @@ public void NestedResources_grandchild_cycle_results_in_binding_failure() } "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); compilation.GetEntrypointSemanticModel().GetAllDiagnostics().ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().HaveDiagnostics(new[] { ("BCP057", DiagnosticLevel.Error, "The name \"grandchild\" does not exist in the current context."), }); @@ -408,7 +408,7 @@ public void NestedResources_ancestors_are_detected() } "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var model = compilation.GetEntrypointSemanticModel(); model.GetAllDiagnostics().ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().BeEmpty(); @@ -457,7 +457,7 @@ public void NestedResources_scopes_isolate_names() } "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var model = compilation.GetEntrypointSemanticModel(); model.GetAllDiagnostics().ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().BeEmpty(); @@ -562,7 +562,7 @@ public void NestedResources_can_get_declared_type_for_property_completion() output hmmmm string = parent::child.properties "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var model = compilation.GetEntrypointSemanticModel(); var output = model.Root.OutputDeclarations.Single(); diff --git a/src/Bicep.Core.IntegrationTests/RegistryTests.cs b/src/Bicep.Core.IntegrationTests/RegistryTests.cs index 851e1c458d2..544ce15d07f 100644 --- a/src/Bicep.Core.IntegrationTests/RegistryTests.cs +++ b/src/Bicep.Core.IntegrationTests/RegistryTests.cs @@ -57,8 +57,9 @@ public async Task InvalidRootCachePathShouldProduceReasonableErrors() RegistryEnabled = true, CacheRootDirectory = badCachePath }; + var featuresFactory = IFeatureProviderFactory.WithStaticFeatureProvider(features); - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, features, BicepTestConstants.ConfigurationManager), BicepTestConstants.ConfigurationManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, featuresFactory, BicepTestConstants.ConfigurationManager), BicepTestConstants.ConfigurationManager); var workspace = new Workspace(); var sourceFileGrouping = SourceFileGroupingBuilder.Build(BicepTestConstants.FileResolver, dispatcher, workspace, fileUri); @@ -67,7 +68,7 @@ public async Task InvalidRootCachePathShouldProduceReasonableErrors() sourceFileGrouping = SourceFileGroupingBuilder.Rebuild(dispatcher, workspace, sourceFileGrouping); } - var compilation = new Compilation(features, BicepTestConstants.NamespaceProvider, sourceFileGrouping, BicepTestConstants.ConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(featuresFactory, BicepTestConstants.NamespaceProvider, sourceFileGrouping, BicepTestConstants.ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetAllDiagnosticsByBicepFile(); diagnostics.Should().HaveCount(1); @@ -170,7 +171,7 @@ public async Task ModuleRestoreContentionShouldProduceConsistentState() features.Setup(m => m.CacheRootDirectory).Returns(cacheDirectory); var configManager = IConfigurationManager.WithStaticConfiguration(BicepTestConstants.BuiltInConfigurationWithAllAnalyzersDisabled); - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(new FileResolver(), clientFactory, templateSpecRepositoryFactory, features.Object, configManager), configManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(new FileResolver(), clientFactory, templateSpecRepositoryFactory, IFeatureProviderFactory.WithStaticFeatureProvider(features.Object), configManager), configManager); var moduleReferences = dataSet.RegistryModules.Values .OrderBy(m => m.Metadata.Target) @@ -222,7 +223,7 @@ public async Task ModuleRestoreWithStuckFileLockShouldFailAfterTimeout(IEnumerab FileResolver fileResolver = new FileResolver(); var configManager = IConfigurationManager.WithStaticConfiguration(BicepTestConstants.BuiltInConfigurationWithAllAnalyzersDisabled); - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(fileResolver, clientFactory, templateSpecRepositoryFactory, features.Object, configManager), configManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(fileResolver, clientFactory, templateSpecRepositoryFactory, IFeatureProviderFactory.WithStaticFeatureProvider(features.Object), configManager), configManager); var configuration = BicepTestConstants.BuiltInConfigurationWithAllAnalyzersDisabled; var moduleReferences = moduleInfos @@ -292,7 +293,7 @@ public async Task ForceModuleRestoreWithStuckFileLockShouldFailAfterTimeout(IEnu FileResolver fileResolver = new FileResolver(); var configManager = IConfigurationManager.WithStaticConfiguration(BicepTestConstants.BuiltInConfigurationWithAllAnalyzersDisabled); - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(fileResolver, clientFactory, templateSpecRepositoryFactory, features.Object, configManager), configManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(fileResolver, clientFactory, templateSpecRepositoryFactory, IFeatureProviderFactory.WithStaticFeatureProvider(features.Object), configManager), configManager); var moduleReferences = moduleInfos .OrderBy(m => m.Metadata.Target) @@ -368,7 +369,7 @@ public async Task ForceModuleRestoreShouldRestoreAllModules(IEnumerable(), new Uri(inputFile), fileResolver, BicepTestConstants.BuiltInOnlyConfigurationManager, features); // the bug was that the compilation would not complete - var compilation = new Compilation(features, BicepTestConstants.NamespaceProvider, sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(IFeatureProviderFactory.WithStaticFeatureProvider(features), BicepTestConstants.NamespaceProvider, sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); compilation.GetEntrypointSemanticModel().GetAllDiagnostics().Should().NotBeEmpty(); } diff --git a/src/Bicep.Core.IntegrationTests/Scenarios/TopLevelResourcePropertiesTests.cs b/src/Bicep.Core.IntegrationTests/Scenarios/TopLevelResourcePropertiesTests.cs index 4f544e694ba..84f29fc0d1a 100644 --- a/src/Bicep.Core.IntegrationTests/Scenarios/TopLevelResourcePropertiesTests.cs +++ b/src/Bicep.Core.IntegrationTests/Scenarios/TopLevelResourcePropertiesTests.cs @@ -26,11 +26,11 @@ public class TopLevelResourcePropertiesTests private static readonly LinterAnalyzer LinterAnalyzer = new(); private static Compilation CreateCompilation(string program) => new( - BicepTestConstants.Features, + BicepTestConstants.FeatureProviderFactory, BuiltInTestTypes.Create(), SourceFileGroupingFactory.CreateFromText(program, new Mock(MockBehavior.Strict).Object), IConfigurationManager.WithStaticConfiguration(Configuration), - BicepTestConstants.ApiVersionProvider, + BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); /// @@ -188,11 +188,11 @@ param inputb string }; var compilation = new Compilation( - BicepTestConstants.Features, + BicepTestConstants.FeatureProviderFactory, BuiltInTestTypes.Create(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, Configuration), IConfigurationManager.WithStaticConfiguration(Configuration), - BicepTestConstants.ApiVersionProvider, + BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); compilation.Should().HaveDiagnostics(new[] { @@ -235,11 +235,11 @@ param inputb string }; var compilation = new Compilation( - BicepTestConstants.Features, + BicepTestConstants.FeatureProviderFactory, BuiltInTestTypes.Create(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, Configuration), IConfigurationManager.WithStaticConfiguration(Configuration), - BicepTestConstants.ApiVersionProvider, + BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); compilation.Should().HaveDiagnostics(new[] { @@ -284,11 +284,11 @@ param inputb string }; var compilation = new Compilation( - BicepTestConstants.Features, + BicepTestConstants.FeatureProviderFactory, BuiltInTestTypes.Create(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, Configuration), IConfigurationManager.WithStaticConfiguration(Configuration), - BicepTestConstants.ApiVersionProvider, + BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); compilation.Should().HaveDiagnostics(new[] { @@ -329,11 +329,11 @@ param inputb string }; var compilation = new Compilation( - BicepTestConstants.Features, + BicepTestConstants.FeatureProviderFactory, BuiltInTestTypes.Create(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, Configuration), IConfigurationManager.WithStaticConfiguration(Configuration), - BicepTestConstants.ApiVersionProvider, + BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); compilation.Should().HaveDiagnostics(new[] { diff --git a/src/Bicep.Core.IntegrationTests/Semantics/CompilationTests.cs b/src/Bicep.Core.IntegrationTests/Semantics/CompilationTests.cs index bfa4bab3bae..cf000aeec55 100644 --- a/src/Bicep.Core.IntegrationTests/Semantics/CompilationTests.cs +++ b/src/Bicep.Core.IntegrationTests/Semantics/CompilationTests.cs @@ -18,7 +18,7 @@ public void EmptyProgram_SourceFileGrouping_should_be_persisted() { var fileResolver = new FileResolver(); var program = SourceFileGroupingFactory.CreateFromText(DataSets.Empty.Bicep, fileResolver); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), program, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), program, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); compilation.SourceFileGrouping.Should().BeSameAs(program); compilation.GetEntrypointSemanticModel().Should().NotBeNull(); diff --git a/src/Bicep.Core.IntegrationTests/Semantics/ParamsSemanticModelTests.cs b/src/Bicep.Core.IntegrationTests/Semantics/ParamsSemanticModelTests.cs index f5ae3468611..f51cb9fdfc7 100644 --- a/src/Bicep.Core.IntegrationTests/Semantics/ParamsSemanticModelTests.cs +++ b/src/Bicep.Core.IntegrationTests/Semantics/ParamsSemanticModelTests.cs @@ -27,12 +27,12 @@ private ParamsSemanticModel CreateSemanticModel(string paramsFilePath) var dispatcher = BicepTestConstants.ModuleDispatcher; var configuration = BicepTestConstants.BuiltInConfiguration; var sourceFileGrouping = SourceFileGroupingBuilder.Build(BicepTestConstants.FileResolver, dispatcher, new Workspace(), PathHelper.FilePathToFileUrl(paramsFilePath)); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, IConfigurationManager.WithStaticConfiguration(configuration), BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, IConfigurationManager.WithStaticConfiguration(configuration), BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); return new ParamsSemanticModel(sourceFileGrouping, configuration, BicepTestConstants.Features, file => { var compilationGrouping = new SourceFileGrouping(BicepTestConstants.FileResolver, file.FileUri, sourceFileGrouping.FileResultByUri, sourceFileGrouping.UriResultByModule, sourceFileGrouping.SourceFileParentLookup); - return new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, compilationGrouping, IConfigurationManager.WithStaticConfiguration(configuration), BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + return new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, compilationGrouping, IConfigurationManager.WithStaticConfiguration(configuration), BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); }); } diff --git a/src/Bicep.Core.IntegrationTests/Semantics/SemanticModelTests.cs b/src/Bicep.Core.IntegrationTests/Semantics/SemanticModelTests.cs index 203c1bbf83f..9c887135363 100644 --- a/src/Bicep.Core.IntegrationTests/Semantics/SemanticModelTests.cs +++ b/src/Bicep.Core.IntegrationTests/Semantics/SemanticModelTests.cs @@ -58,7 +58,7 @@ public async Task ProgramsShouldProduceExpectedDiagnostics(DataSet dataSet) [TestMethod] public void EndOfFileFollowingSpaceAfterParameterKeyWordShouldNotThrow() { - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText("parameter ", BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText("parameter ", BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); FluentActions.Invoking(() => compilation.GetEntrypointSemanticModel().GetAllDiagnostics()).Should().NotThrow(); } @@ -191,7 +191,7 @@ public void GetAllDiagnostics_VerifyDisableNextLineDiagnosticsDirectiveDoesNotSu [uri] = bicepFileContents, }; - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics(); diagnostics.Count().Should().Be(2); @@ -237,7 +237,7 @@ public void GetAllDiagnostics_VerifyDisableNextLineDiagnosticsDirectiveSupportsC [uri] = bicepFileContents, }; - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); compilation.GetEntrypointSemanticModel().GetAllDiagnostics().Should().BeEmpty(); } @@ -256,7 +256,7 @@ public void GetAllDiagnostics_VerifyDisableNextLineDiagnosticsDirectiveSupportsL [uri] = bicepFileContents, }; - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); compilation.GetEntrypointSemanticModel().GetAllDiagnostics().Should().BeEmpty(); } @@ -276,7 +276,7 @@ public void GetAllDiagnostics_WithNoDisableNextLineDiagnosticsDirectiveInPreviou [uri] = bicepFileContents, }; - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); compilation.GetEntrypointSemanticModel().GetAllDiagnostics().Count().Should().Be(1); } diff --git a/src/Bicep.Core.IntegrationTests/TypeSystem/TypeValidationTests.cs b/src/Bicep.Core.IntegrationTests/TypeSystem/TypeValidationTests.cs index ed5f10b859a..6ba0e1873d3 100644 --- a/src/Bicep.Core.IntegrationTests/TypeSystem/TypeValidationTests.cs +++ b/src/Bicep.Core.IntegrationTests/TypeSystem/TypeValidationTests.cs @@ -23,11 +23,11 @@ private static SemanticModel GetSemanticModelForTest(string programText, INamesp { var configuration = BicepTestConstants.BuiltInConfigurationWithAllAnalyzersDisabled; var compilation = new Compilation( - BicepTestConstants.Features, + BicepTestConstants.FeatureProviderFactory, nsProvider, SourceFileGroupingFactory.CreateFromText(programText, BicepTestConstants.FileResolver), IConfigurationManager.WithStaticConfiguration(configuration), - BicepTestConstants.ApiVersionProvider, + BicepTestConstants.ApiVersionProviderFactory, new LinterAnalyzer()); return compilation.GetEntrypointSemanticModel(); diff --git a/src/Bicep.Core.Samples/DataSetsExtensions.cs b/src/Bicep.Core.Samples/DataSetsExtensions.cs index 892ed5f765b..2dbce3b2c47 100644 --- a/src/Bicep.Core.Samples/DataSetsExtensions.cs +++ b/src/Bicep.Core.Samples/DataSetsExtensions.cs @@ -42,6 +42,7 @@ public static string SaveFilesToTestDirectory(this DataSet dataSet, TestContext public static async Task<(Compilation compilation, string outputDirectory, Uri fileUri)> SetupPrerequisitesAndCreateCompilation(this DataSet dataSet, TestContext testContext) { var features = BicepTestConstants.CreateFeatureProvider(testContext, registryEnabled: dataSet.HasExternalModules); + var featuresFactory = IFeatureProviderFactory.WithStaticFeatureProvider(features); var outputDirectory = dataSet.SaveFilesToTestDirectory(testContext); var clientFactory = dataSet.CreateMockRegistryClients(testContext); await dataSet.PublishModulesToRegistryAsync(clientFactory, testContext); @@ -49,7 +50,7 @@ public static string SaveFilesToTestDirectory(this DataSet dataSet, TestContext var fileUri = PathHelper.FilePathToFileUrl(Path.Combine(outputDirectory, DataSet.TestFileMain)); var configuration = BicepTestConstants.ConfigurationManager.GetConfiguration(fileUri).WithAnalyzersDisabled(BicepTestConstants.AnalyzerRulesToDisableInTests); var configManager = IConfigurationManager.WithStaticConfiguration(configuration); - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, features, configManager), configManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, featuresFactory, configManager), configManager); var workspace = new Workspace(); var namespaceProvider = new DefaultNamespaceProvider(new AzResourceTypeLoader()); var sourceFileGrouping = SourceFileGroupingBuilder.Build(BicepTestConstants.FileResolver, dispatcher, workspace, fileUri); @@ -58,15 +59,16 @@ public static string SaveFilesToTestDirectory(this DataSet dataSet, TestContext sourceFileGrouping = SourceFileGroupingBuilder.Rebuild(dispatcher, workspace, sourceFileGrouping); } - return (new Compilation(features, namespaceProvider, sourceFileGrouping, IConfigurationManager.WithStaticConfiguration(configuration), BicepTestConstants.ApiVersionProvider, new LinterAnalyzer()), outputDirectory, fileUri); + return (new Compilation(featuresFactory, namespaceProvider, sourceFileGrouping, IConfigurationManager.WithStaticConfiguration(configuration), BicepTestConstants.ApiVersionProviderFactory, new LinterAnalyzer()), outputDirectory, fileUri); } public static IContainerRegistryClientFactory CreateMockRegistryClients(this DataSet dataSet, TestContext testContext, params (Uri registryUri, string repository)[] additionalClients) { var clientsBuilder = ImmutableDictionary.CreateBuilder<(Uri registryUri, string repository), MockRegistryBlobClient>(); var configManager = IConfigurationManager.WithStaticConfiguration(BicepTestConstants.BuiltInConfigurationWithAllAnalyzersDisabled); + var featuresFactory = IFeatureProviderFactory.WithStaticFeatureProvider(BicepTestConstants.CreateFeatureProvider(testContext, registryEnabled: dataSet.HasRegistryModules)); - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.CreateFeatureProvider(testContext, registryEnabled: dataSet.HasRegistryModules), configManager), configManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, featuresFactory, configManager), configManager); foreach (var (moduleName, publishInfo) in dataSet.RegistryModules) { @@ -105,7 +107,8 @@ public static IContainerRegistryClientFactory CreateMockRegistryClients(this Dat public static ITemplateSpecRepositoryFactory CreateMockTemplateSpecRepositoryFactory(this DataSet dataSet, TestContext testContext) { var configManager = IConfigurationManager.WithStaticConfiguration(BicepTestConstants.BuiltInConfigurationWithAllAnalyzersDisabled); - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.CreateFeatureProvider(testContext, registryEnabled: dataSet.HasRegistryModules), configManager), configManager); + var featuresFactory = IFeatureProviderFactory.WithStaticFeatureProvider(BicepTestConstants.CreateFeatureProvider(testContext, registryEnabled: dataSet.HasRegistryModules)); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, featuresFactory, configManager), configManager); var repositoryMocksBySubscription = new Dictionary>(); foreach (var (moduleName, templateSpecInfo) in dataSet.TemplateSpecs) @@ -135,7 +138,8 @@ public static ITemplateSpecRepositoryFactory CreateMockTemplateSpecRepositoryFac public static async Task PublishModulesToRegistryAsync(this DataSet dataSet, IContainerRegistryClientFactory clientFactory, TestContext testContext) { var configManager = IConfigurationManager.WithStaticConfiguration(BicepTestConstants.BuiltInConfigurationWithAllAnalyzersDisabled); - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.CreateFeatureProvider(testContext, registryEnabled: dataSet.HasRegistryModules), configManager), configManager); + var featuresFactory = IFeatureProviderFactory.WithStaticFeatureProvider(BicepTestConstants.CreateFeatureProvider(testContext, registryEnabled: dataSet.HasRegistryModules)); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, featuresFactory, configManager), configManager); foreach (var (moduleName, publishInfo) in dataSet.RegistryModules) { diff --git a/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs b/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs index 642610288ef..594a2ed0a36 100644 --- a/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs +++ b/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs @@ -82,6 +82,6 @@ public void GetResourceTypeNames_SeparateScopes() } private ApiVersionProvider CreateDefaultApiVersionProvider() - => new ApiVersionProvider(new FeatureProvider(), new DefaultNamespaceProvider(new AzResourceTypeLoader())); + => new ApiVersionProvider(IFeatureProviderFactory.FeatureDefaults, new DefaultNamespaceProvider(new AzResourceTypeLoader())); } } diff --git a/src/Bicep.Core.UnitTests/BicepTestConstants.cs b/src/Bicep.Core.UnitTests/BicepTestConstants.cs index 94d58a4b0e1..c712e60df3d 100644 --- a/src/Bicep.Core.UnitTests/BicepTestConstants.cs +++ b/src/Bicep.Core.UnitTests/BicepTestConstants.cs @@ -44,6 +44,8 @@ public static class BicepTestConstants public static readonly TestFeatureProvider Features = CreateFeatureProvider(registryEnabled: false, symbolicNameCodegenEnabled: false, importsEnabled: false, resourceTypedParamsAndOutputsEnabled: false, sourceMappingEnabled: false, paramsFilesEnabled: false, assemblyFileVersion: BicepTestConstants.DevAssemblyFileVersion); + public static readonly IFeatureProviderFactory FeatureProviderFactory = IFeatureProviderFactory.WithStaticFeatureProvider(Features); + public static readonly EmitterSettings EmitterSettings = new EmitterSettings(Features); public static readonly IAzResourceTypeLoader AzResourceTypeLoader = new AzResourceTypeLoader(); @@ -70,7 +72,7 @@ public static class BicepTestConstants public static readonly IConfigurationManager BuiltInOnlyConfigurationManager = IConfigurationManager.WithStaticConfiguration(BuiltInConfiguration); - public static readonly IModuleRegistryProvider RegistryProvider = new DefaultModuleRegistryProvider(FileResolver, ClientFactory, TemplateSpecRepositoryFactory, Features, BuiltInOnlyConfigurationManager); + public static readonly IModuleRegistryProvider RegistryProvider = new DefaultModuleRegistryProvider(FileResolver, ClientFactory, TemplateSpecRepositoryFactory, FeatureProviderFactory, BuiltInOnlyConfigurationManager); public static readonly IModuleDispatcher ModuleDispatcher = new ModuleDispatcher(BicepTestConstants.RegistryProvider, IConfigurationManager.WithStaticConfiguration(BuiltInConfiguration)); @@ -79,6 +81,7 @@ public static class BicepTestConstants public static readonly IModuleRestoreScheduler ModuleRestoreScheduler = CreateMockModuleRestoreScheduler(); public static readonly ApiVersionProvider ApiVersionProvider = new ApiVersionProvider(Features, new DefaultNamespaceProvider(new AzResourceTypeLoader())); + public static readonly IApiVersionProviderFactory ApiVersionProviderFactory = IApiVersionProviderFactory.WithStaticApiVersionProvider(ApiVersionProvider); public static RootConfiguration CreateMockConfiguration(Dictionary? customConfigurationData = null, string? configurationPath = null) { @@ -90,6 +93,7 @@ public static RootConfiguration CreateMockConfiguration(Dictionary(), ["analyzers"] = new Dictionary(), + ["experimentalFeaturesEnabled"] = new Dictionary(), }; if (customConfigurationData is not null) diff --git a/src/Bicep.Core.UnitTests/Configuration/ConfigurationManagerTests.cs b/src/Bicep.Core.UnitTests/Configuration/ConfigurationManagerTests.cs index 902b12a65b8..4aede226745 100644 --- a/src/Bicep.Core.UnitTests/Configuration/ConfigurationManagerTests.cs +++ b/src/Bicep.Core.UnitTests/Configuration/ConfigurationManagerTests.cs @@ -94,7 +94,8 @@ public void GetBuiltInConfiguration_NoParameter_ReturnsBuiltInConfigurationWithA } } } - } + }, + ""experimentalFeaturesEnabled"": {} }"); } @@ -144,7 +145,8 @@ public void GetBuiltInConfiguration_DisableAllAnalyzers_ReturnsBuiltInConfigurat } } }, - ""analyzers"": {} + ""analyzers"": {}, + ""experimentalFeaturesEnabled"": {} }"); } @@ -220,7 +222,8 @@ public void GetBuiltInConfiguration_DisableAnalyzers_ReturnsBuiltInConfiguration } } } - } + }, + ""experimentalFeaturesEnabled"": {} }"); } @@ -431,6 +434,10 @@ public void GetConfiguration_ValidCustomConfiguration_OverridesBuiltInConfigurat } } } + }, + ""cacheRootDirectory"": ""/home/username/.bicep/cache"", + ""experimentalFeaturesEnabled"": { + ""registry"": false } }" }); @@ -512,6 +519,10 @@ public void GetConfiguration_ValidCustomConfiguration_OverridesBuiltInConfigurat } } } + }, + ""cacheRootDirectory"": ""/home/username/.bicep/cache"", + ""experimentalFeaturesEnabled"": { + ""registry"": false } }"); } diff --git a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/UseRecentApiVersionRuleTests.cs b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/UseRecentApiVersionRuleTests.cs index eb231bdd152..8ac9de95770 100644 --- a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/UseRecentApiVersionRuleTests.cs +++ b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/UseRecentApiVersionRuleTests.cs @@ -108,6 +108,8 @@ private static RootConfiguration CreateConfigurationWithFakeToday(string today) } } }".Replace("", today))), + BicepTestConstants.BuiltInConfiguration.CacheRootDirectory, + BicepTestConstants.BuiltInConfiguration.ExperimentalFeaturesEnabled, null, null); } diff --git a/src/Bicep.Core.UnitTests/Emit/ExpressionConverterTests.cs b/src/Bicep.Core.UnitTests/Emit/ExpressionConverterTests.cs index 001b97f61c8..60c74bef3c7 100644 --- a/src/Bicep.Core.UnitTests/Emit/ExpressionConverterTests.cs +++ b/src/Bicep.Core.UnitTests/Emit/ExpressionConverterTests.cs @@ -60,7 +60,7 @@ public class ExpressionConverterTests public void ShouldConvertExpressionsCorrectly(string text, string expected) { var programText = $"var test = {text}"; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(programText, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(programText, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var programSyntax = compilation.SourceFileGrouping.EntryPoint.ProgramSyntax; var variableDeclarationSyntax = programSyntax.Children.OfType().First(); diff --git a/src/Bicep.Core.UnitTests/Emit/InlineDependencyVisitorTests.cs b/src/Bicep.Core.UnitTests/Emit/InlineDependencyVisitorTests.cs index ae0d97da179..34e263ddbf9 100644 --- a/src/Bicep.Core.UnitTests/Emit/InlineDependencyVisitorTests.cs +++ b/src/Bicep.Core.UnitTests/Emit/InlineDependencyVisitorTests.cs @@ -26,7 +26,7 @@ public class InlineDependencyVisitorTests [TestMethod] public void VisitorShouldCalculateInliningInBulk() { - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateFromText(Text, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateFromText(Text, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var inlineVariables = InlineDependencyVisitor.GetVariablesToInline(compilation.GetEntrypointSemanticModel()); @@ -43,7 +43,7 @@ public void VisitorShouldCalculateInliningInBulk() [DataTestMethod] public void VisitorShouldProduceNoChainForNonInlinedVariables(string variableName) { - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateFromText(Text, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateFromText(Text, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); VariableDeclarationSyntax variable = GetVariableByName(compilation, variableName); InlineDependencyVisitor.ShouldInlineVariable(compilation.GetEntrypointSemanticModel(), variable, out var chain).Should().BeFalse(); @@ -57,7 +57,7 @@ public void VisitorShouldProduceNoChainForNonInlinedVariables(string variableNam [DataTestMethod] public void VisitorShouldProduceCorrectChainForInlinedVariables(string variableName, string expectedChain) { - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateFromText(Text, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateFromText(Text, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); VariableDeclarationSyntax variable = GetVariableByName(compilation, variableName); InlineDependencyVisitor.ShouldInlineVariable(compilation.GetEntrypointSemanticModel(), variable, out var chain).Should().BeTrue(); diff --git a/src/Bicep.Core.UnitTests/Features/FeatureProviderTests.cs b/src/Bicep.Core.UnitTests/Features/FeatureProviderTests.cs new file mode 100644 index 00000000000..e1e3f5ee79c --- /dev/null +++ b/src/Bicep.Core.UnitTests/Features/FeatureProviderTests.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.IO.Abstractions.TestingHelpers; +using System.Linq; +using Bicep.Core.Configuration; +using Bicep.Core.Features; +using Bicep.Core.UnitTests.Assertions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace Bicep.Core.UnitTests.Features; + +[TestClass] +public class FeatureProviderTests +{ + [NotNull] + public TestContext? TestContext { get; set; } + + [TestMethod] + public void PropertyLookup_FallsBackToDefaultIfNoSourcesSupplyIt() + { + var sources = Enumerable.Range(0, 3).Select(i => { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(s => s.Priority).Returns((sbyte) i); + mock.Setup(s => s.ImportsEnabled).Returns(new Nullable()); + return mock; + }).ToArray(); + var defaults = new Mock(MockBehavior.Strict); + defaults.Setup(d => d.ImportsEnabled).Returns(true); + + var sut = new FeatureProvider(defaults.Object, sources.Select(s => s.Object)); + sut.ImportsEnabled.Should().BeTrue(); + + foreach (var source in sources) + { + source.Verify(s => s.ImportsEnabled, Times.Once); + } + } + + [TestMethod] + public void PropertyLookup_PollsSourcesInPriorityOrder() + { + var sources = Enumerable.Range(1, 3).Select(i => { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(s => s.Priority).Returns((sbyte) -i); + mock.Setup(s => s.ImportsEnabled).Returns(i == 3 ? new Nullable(true) : new Nullable(false)); + return mock; + }).ToArray(); + var defaults = new Mock(MockBehavior.Strict); + + var sut = new FeatureProvider(defaults.Object, sources.Select(s => s.Object)); + sut.ImportsEnabled.Should().BeTrue(); + + defaults.Verify(d => d.ImportsEnabled, Times.Never); + sources[^1].Verify(s => s.ImportsEnabled, Times.Once); + foreach(var source in sources[..^1]) + { + source.Verify(s => s.ImportsEnabled, Times.Never); + } + } + + [TestMethod] + public void PropertyLookup_WithNothingConfigured_ReturnsDefault() + { + var fileSystem = new MockFileSystem(new Dictionary + { + [CreatePath("repo")] = new MockDirectoryData(), + [CreatePath("repo/bicepconfig.json")] = @"{""experimentalFeaturesEnabled"": {}}", + }); + var configManager = new ConfigurationManager(fileSystem); + var configuration = configManager.GetConfiguration(new Uri(this.CreatePath("repo/main.bicep"))); + var fpm = new FeatureProviderFactory(configManager); + + var control = fpm.GetFeatureProvider(new Uri("inmemory:///main.bicp")); + var sut = fpm.GetFeatureProvider(new Uri(this.CreatePath("repo/main.bicep"))); + sut.ImportsEnabled.Should().Be(control.ImportsEnabled); + } + + [TestMethod] + public void PropertyLookup_WithFeatureEnabledViaBicepConfig_ReturnsTrue() + { + var fileSystem = new MockFileSystem(new Dictionary + { + [CreatePath("repo")] = new MockDirectoryData(), + [CreatePath("repo/bicepconfig.json")] = @"{""experimentalFeaturesEnabled"": {}}", + [CreatePath("repo/subdir")] = new MockDirectoryData(), + [CreatePath("repo/subdir/bicepconfig.json")] = @"{""experimentalFeaturesEnabled"": {""imports"": true}}", + }); + var configManager = new ConfigurationManager(fileSystem); + var configuration = configManager.GetConfiguration(new Uri(this.CreatePath("repo/main.bicep"))); + var fpm = new FeatureProviderFactory(configManager); + + var control = fpm.GetFeatureProvider(new Uri("inmemory:///main.bicp")); + control.ImportsEnabled.Should().BeFalse(); + var mainDirFeatures = fpm.GetFeatureProvider(new Uri(this.CreatePath("repo/main.bicep"))); + mainDirFeatures.ImportsEnabled.Should().BeFalse(); + var subDirFeatures = fpm.GetFeatureProvider(new Uri(this.CreatePath("repo/subdir/module.bicep"))); + subDirFeatures.ImportsEnabled.Should().BeTrue(); + } + + private string CreatePath(string path) => Path.Combine(this.TestContext.ResultsDirectory, path.Replace('/', Path.DirectorySeparatorChar)); +} diff --git a/src/Bicep.Core.UnitTests/Semantics/SymbolContextTests.cs b/src/Bicep.Core.UnitTests/Semantics/SymbolContextTests.cs index 9ba05404578..989ec0eb4d9 100644 --- a/src/Bicep.Core.UnitTests/Semantics/SymbolContextTests.cs +++ b/src/Bicep.Core.UnitTests/Semantics/SymbolContextTests.cs @@ -19,7 +19,7 @@ public void LockedModeShouldBlockAccess() { const string expectedMessage = "Properties of the symbol context should not be accessed until name binding is completed."; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText("", BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText("", BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var bindings = new Dictionary(); var cyclesBySymbol = new Dictionary>(); var context = new SymbolContext(compilation, compilation.GetEntrypointSemanticModel()); diff --git a/src/Bicep.Core.UnitTests/Tracing/AzureEventSourceListenerFactoryTests.cs b/src/Bicep.Core.UnitTests/Tracing/AzureEventSourceListenerFactoryTests.cs index e312cb90279..0f1adb6a130 100644 --- a/src/Bicep.Core.UnitTests/Tracing/AzureEventSourceListenerFactoryTests.cs +++ b/src/Bicep.Core.UnitTests/Tracing/AzureEventSourceListenerFactoryTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Bicep.Core.Features; using Bicep.Core.Tracing; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -49,7 +50,7 @@ public class AzureEventSourceListenerFactoryTests public void FullVerbosityShouldReturnNull() { AzureEventSourceListenerFactory.GetFormattedMessageWithoutHeaders( - Features.TraceVerbosity.Full, + TraceVerbosity.Full, AzureCoreEventSource, FormatString, SampleNamesWithHeaders, @@ -60,7 +61,7 @@ public void FullVerbosityShouldReturnNull() public void NonAzureCoreShouldReturnNull() { AzureEventSourceListenerFactory.GetFormattedMessageWithoutHeaders( - Features.TraceVerbosity.Basic, + TraceVerbosity.Basic, "not-azure-core", FormatString, SampleNamesWithHeaders, @@ -71,7 +72,7 @@ public void NonAzureCoreShouldReturnNull() public void NonHeaderEventShouldReturnNull() { AzureEventSourceListenerFactory.GetFormattedMessageWithoutHeaders( - Features.TraceVerbosity.Basic, + TraceVerbosity.Basic, AzureCoreEventSource, FormatString, SampleNamesWithoutHeaders, @@ -82,7 +83,7 @@ public void NonHeaderEventShouldReturnNull() public void HeadersShouldBeRemoved() { AzureEventSourceListenerFactory.GetFormattedMessageWithoutHeaders( - Features.TraceVerbosity.Basic, + TraceVerbosity.Basic, AzureCoreEventSource, FormatString, SampleNamesWithHeaders, diff --git a/src/Bicep.Core.UnitTests/TypeSystem/Az/AzResourceTypeProviderTests.cs b/src/Bicep.Core.UnitTests/TypeSystem/Az/AzResourceTypeProviderTests.cs index e6c54f2b7d2..0d830c532fc 100644 --- a/src/Bicep.Core.UnitTests/TypeSystem/Az/AzResourceTypeProviderTests.cs +++ b/src/Bicep.Core.UnitTests/TypeSystem/Az/AzResourceTypeProviderTests.cs @@ -143,7 +143,7 @@ public void AzResourceTypeProvider_should_warn_for_missing_resource_types() { var configuration = BicepTestConstants.BuiltInConfigurationWithAllAnalyzersDisabled; Compilation createCompilation(string program) - => new Compilation(BicepTestConstants.Features, new DefaultNamespaceProvider(new AzResourceTypeLoader()), SourceFileGroupingFactory.CreateFromText(program, new Mock(MockBehavior.Strict).Object), IConfigurationManager.WithStaticConfiguration(configuration), BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + => new Compilation(BicepTestConstants.FeatureProviderFactory, new DefaultNamespaceProvider(new AzResourceTypeLoader()), SourceFileGroupingFactory.CreateFromText(program, new Mock(MockBehavior.Strict).Object), IConfigurationManager.WithStaticConfiguration(configuration), BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); // Missing top-level properties - should be an error var compilation = createCompilation(@" @@ -160,7 +160,7 @@ Compilation createCompilation(string program) public void AzResourceTypeProvider_should_error_for_top_level_system_properties_and_warn_for_rest() { Compilation createCompilation(string program) - => new Compilation(BicepTestConstants.Features, BuiltInTestTypes.Create(), SourceFileGroupingFactory.CreateFromText(program, new Mock(MockBehavior.Strict).Object), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + => new Compilation(BicepTestConstants.FeatureProviderFactory, BuiltInTestTypes.Create(), SourceFileGroupingFactory.CreateFromText(program, new Mock(MockBehavior.Strict).Object), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); // Missing top-level properties - should be an error var compilation = createCompilation(@" diff --git a/src/Bicep.Core.UnitTests/Utils/CompilationHelper.cs b/src/Bicep.Core.UnitTests/Utils/CompilationHelper.cs index 87edae922b1..7c04210e438 100644 --- a/src/Bicep.Core.UnitTests/Utils/CompilationHelper.cs +++ b/src/Bicep.Core.UnitTests/Utils/CompilationHelper.cs @@ -83,7 +83,7 @@ public static CompilationResult Compile(Uri entryUri, IReadOnlyDictionary { var compilationGrouping = new SourceFileGrouping(fileResolver, file.FileUri, sourceFileGrouping.FileResultByUri, sourceFileGrouping.UriResultByModule, sourceFileGrouping.SourceFileParentLookup); - return new Compilation(context.GetFeatures(), context.GetNamespaceProvider(), compilationGrouping, IConfigurationManager.WithStaticConfiguration(configuration), apiVersionProvider, new LinterAnalyzer()); + return new Compilation(IFeatureProviderFactory.WithStaticFeatureProvider(context.GetFeatures()), context.GetNamespaceProvider(), compilationGrouping, IConfigurationManager.WithStaticConfiguration(configuration), IApiVersionProviderFactory.WithStaticApiVersionProvider(apiVersionProvider), new LinterAnalyzer()); }); return CompileParams(context, model); @@ -153,7 +153,7 @@ public static ParamsCompilationResult CompileParams(CompilationHelperContext con var model = new ParamsSemanticModel(sourceFileGrouping, configuration, context.GetFeatures(), file => { var compilationGrouping = new SourceFileGrouping(fileResolver, file.FileUri, sourceFileGrouping.FileResultByUri, sourceFileGrouping.UriResultByModule, sourceFileGrouping.SourceFileParentLookup); - return new Compilation(context.GetFeatures(), context.GetNamespaceProvider(), compilationGrouping, IConfigurationManager.WithStaticConfiguration(configuration), apiVersionProvider, new LinterAnalyzer()); + return new Compilation(IFeatureProviderFactory.WithStaticFeatureProvider(context.GetFeatures()), context.GetNamespaceProvider(), compilationGrouping, IConfigurationManager.WithStaticConfiguration(configuration), IApiVersionProviderFactory.WithStaticApiVersionProvider(apiVersionProvider), new LinterAnalyzer()); }); return CompileParams(context, model); diff --git a/src/Bicep.Core.UnitTests/Utils/SourceFileGroupingFactory.cs b/src/Bicep.Core.UnitTests/Utils/SourceFileGroupingFactory.cs index b8544751347..889d195c74e 100644 --- a/src/Bicep.Core.UnitTests/Utils/SourceFileGroupingFactory.cs +++ b/src/Bicep.Core.UnitTests/Utils/SourceFileGroupingFactory.cs @@ -37,7 +37,7 @@ public static SourceFileGrouping CreateForFiles(IReadOnlyDictionary fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, - features, + IFeatureProviderFactory.WithStaticFeatureProvider(features), configurationManager), configurationManager), workspace, diff --git a/src/Bicep.Core.UnitTests/Workspaces/BicepFileTests.cs b/src/Bicep.Core.UnitTests/Workspaces/BicepFileTests.cs index 08ccf0455d8..3aa2c147a4a 100644 --- a/src/Bicep.Core.UnitTests/Workspaces/BicepFileTests.cs +++ b/src/Bicep.Core.UnitTests/Workspaces/BicepFileTests.cs @@ -16,7 +16,7 @@ public void VerifyDisableNextLineDiagnosticDirectivesCache_WithDisableNextLineDi { string bicepFileContents = @"#disable-next-line no-unused-params param storageAccount string = 'testStorageAccount'"; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(bicepFileContents, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(bicepFileContents, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var bicepFile = compilation.GetEntrypointSemanticModel().SourceFile; var disabledDiagnosticsCache = bicepFile.DisabledDiagnosticsCache; @@ -31,7 +31,7 @@ public void VerifyDisableNextLineDiagnosticDirectivesCache_WithDisableNextLineDi public void VerifyDisableNextLineDiagnosticDirectivesCache_WithNoDisableNextLineDiagnosticDirectivesInBicepFile() { string bicepFileContents = @"param storageAccount string = 'testStorageAccount'"; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(bicepFileContents, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(bicepFileContents, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var bicepFile = compilation.GetEntrypointSemanticModel().SourceFile; var disabledDiagnosticsCache = bicepFile.DisabledDiagnosticsCache; @@ -64,7 +64,7 @@ public void VerifyDisableNextLineDiagnosticDirectivesCache_WithMultipleDisableNe "; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(bicepFileContents, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(bicepFileContents, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var bicepFile = compilation.GetEntrypointSemanticModel().SourceFile; var disabledDiagnosticsCache = bicepFile.DisabledDiagnosticsCache; diff --git a/src/Bicep.Core/Analyzers/Linter/ApiVersions/ApiVersionProviderFactory.cs b/src/Bicep.Core/Analyzers/Linter/ApiVersions/ApiVersionProviderFactory.cs new file mode 100644 index 00000000000..b7554cd74ac --- /dev/null +++ b/src/Bicep.Core/Analyzers/Linter/ApiVersions/ApiVersionProviderFactory.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System; +using System.Runtime.CompilerServices; +using Bicep.Core.Features; +using Bicep.Core.Semantics.Namespaces; + +namespace Bicep.Core.Analyzers.Linter.ApiVersions; + +public class ApiVersionProviderFactory : IApiVersionProviderFactory +{ + private readonly ConditionalWeakTable apiVersionProviderCache = new(); + private readonly IFeatureProviderFactory featureProviderFactory; + private readonly INamespaceProvider namespaceProvider; + + public ApiVersionProviderFactory(IFeatureProviderFactory featureProviderFactory, INamespaceProvider namespaceProvider) + { + this.featureProviderFactory = featureProviderFactory; + this.namespaceProvider = namespaceProvider; + } + + public IApiVersionProvider GetApiVersionProvider(Uri templateUri) + => apiVersionProviderCache.GetValue(featureProviderFactory.GetFeatureProvider(templateUri), + features => new ApiVersionProvider(features, namespaceProvider)); +} diff --git a/src/Bicep.Core/Analyzers/Linter/ApiVersions/IApiVersionProviderFactory.cs b/src/Bicep.Core/Analyzers/Linter/ApiVersions/IApiVersionProviderFactory.cs new file mode 100644 index 00000000000..f6db42cc3e2 --- /dev/null +++ b/src/Bicep.Core/Analyzers/Linter/ApiVersions/IApiVersionProviderFactory.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System; + +namespace Bicep.Core.Analyzers.Linter.ApiVersions; + +public interface IApiVersionProviderFactory +{ + IApiVersionProvider GetApiVersionProvider(Uri templateUri); + + static IApiVersionProviderFactory WithStaticApiVersionProvider(IApiVersionProvider apiVersionProvider) + => new ConstantApiVersionProviderFactory(apiVersionProvider); + + private class ConstantApiVersionProviderFactory : IApiVersionProviderFactory + { + private readonly IApiVersionProvider apiVersionProvider; + + internal ConstantApiVersionProviderFactory(IApiVersionProvider apiVersionProvider) + { + this.apiVersionProvider = apiVersionProvider; + } + + public IApiVersionProvider GetApiVersionProvider(Uri templateUri) => apiVersionProvider; + } +} diff --git a/src/Bicep.Core/Analyzers/Linter/Rules/UseRecentApiVersionRule.cs b/src/Bicep.Core/Analyzers/Linter/Rules/UseRecentApiVersionRule.cs index 0b0c363503b..233a52395a0 100644 --- a/src/Bicep.Core/Analyzers/Linter/Rules/UseRecentApiVersionRule.cs +++ b/src/Bicep.Core/Analyzers/Linter/Rules/UseRecentApiVersionRule.cs @@ -121,7 +121,7 @@ override public IEnumerable AnalyzeInternal(SemanticModel model) if (functionCallInfo.ApiVersion.HasValue && functionCallInfo.ResourceType is not null) { return AnalyzeApiVersion( - model.Compilation.ApiVersionProvider, + model.ApiVersionProvider, today, errorSpan: functionCallInfo.FunctionCallSyntax.Span, replacementSpan: null, @@ -299,7 +299,7 @@ private static string GetResourceTypeFromResourceId(SemanticModel model, string var mostRecentValid = resourceId; while (resourceTypeRegex.IsMatch(resourceType)) { - if (model.Compilation.ApiVersionProvider.GetApiVersions(model.TargetScope, resourceType).Any()) + if (model.ApiVersionProvider.GetApiVersions(model.TargetScope, resourceType).Any()) { // The resource type exists return resourceType; @@ -325,7 +325,7 @@ resourceTypeReference.ApiVersion is string apiVersionString && { string fullyQualifiedResourceType = resourceTypeReference.FormatType(); return AnalyzeApiVersion( - model.Compilation.ApiVersionProvider, + model.ApiVersionProvider, today, replacementSpan, replacementSpan, diff --git a/src/Bicep.Core/Configuration/AnalyzersConfigurationExtensions.cs b/src/Bicep.Core/Configuration/AnalyzersConfigurationExtensions.cs index 09bd2e09fda..41f1b44630b 100644 --- a/src/Bicep.Core/Configuration/AnalyzersConfigurationExtensions.cs +++ b/src/Bicep.Core/Configuration/AnalyzersConfigurationExtensions.cs @@ -27,16 +27,20 @@ public static RootConfiguration WithAllAnalyzersDisabled(this RootConfiguration analyzersConfiguration.Cloud, analyzersConfiguration.ModuleAliases, analyzersConfiguration.Analyzers.WithAllAnalyzersDisabled(), + analyzersConfiguration.CacheRootDirectory, + analyzersConfiguration.ExperimentalFeaturesEnabled, analyzersConfiguration.ConfigurationPath, analyzersConfiguration.DiagnosticBuilders); } - public static RootConfiguration WithAnalyzersDisabled(this RootConfiguration analyzersConfiguration, params string[] analyzerCodesToDisable) + public static RootConfiguration WithAnalyzersDisabled(this RootConfiguration analyzersConfiguration, params string[] analyzerCodesToDisable) { return new RootConfiguration( analyzersConfiguration.Cloud, analyzersConfiguration.ModuleAliases, analyzersConfiguration.Analyzers.WithAnalyzersDisabled(analyzerCodesToDisable), + analyzersConfiguration.CacheRootDirectory, + analyzersConfiguration.ExperimentalFeaturesEnabled, analyzersConfiguration.ConfigurationPath, analyzersConfiguration.DiagnosticBuilders); } diff --git a/src/Bicep.Core/Configuration/ConfigurationManager.cs b/src/Bicep.Core/Configuration/ConfigurationManager.cs index 97643e7397a..d6df85ed324 100644 --- a/src/Bicep.Core/Configuration/ConfigurationManager.cs +++ b/src/Bicep.Core/Configuration/ConfigurationManager.cs @@ -95,7 +95,7 @@ private static RootConfiguration WithLoadDiagnostics(RootConfiguration configura { if (diagnostics.Count > 0) { - return new(configuration.Cloud, configuration.ModuleAliases, configuration.Analyzers, configuration.ConfigurationPath, diagnostics); + return new(configuration.Cloud, configuration.ModuleAliases, configuration.Analyzers, configuration.CacheRootDirectory, configuration.ExperimentalFeaturesEnabled, configuration.ConfigurationPath, diagnostics); } return configuration; diff --git a/src/Bicep.Core/Configuration/ExperimentalFeaturesEnabledConfiguration.cs b/src/Bicep.Core/Configuration/ExperimentalFeaturesEnabledConfiguration.cs new file mode 100644 index 00000000000..4e1356b4dfe --- /dev/null +++ b/src/Bicep.Core/Configuration/ExperimentalFeaturesEnabledConfiguration.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Immutable; +using System.Text.Json; + +namespace Bicep.Core.Configuration; + +public class ExperimentalFeaturesEnabledConfiguration : ConfigurationSection> +{ + public ExperimentalFeaturesEnabledConfiguration(JsonElement data) : base(data.EnumerateObject() + .ToImmutableDictionary(p => p.Name, p => p.Value.GetBoolean(), StringComparer.OrdinalIgnoreCase)) {} + + public bool? IsEnabled(string featureName) => Data.TryGetValue(featureName, out var value) ? value : default; +} diff --git a/src/Bicep.Core/Configuration/RootConfiguration.cs b/src/Bicep.Core/Configuration/RootConfiguration.cs index 2f7de1854b0..4826a2e7a85 100644 --- a/src/Bicep.Core/Configuration/RootConfiguration.cs +++ b/src/Bicep.Core/Configuration/RootConfiguration.cs @@ -12,22 +12,39 @@ namespace Bicep.Core.Configuration { public class RootConfiguration { - public RootConfiguration(CloudConfiguration cloud, ModuleAliasesConfiguration moduleAliases, AnalyzersConfiguration analyzers, string? configurationPath, IEnumerable? diagnosticBuilders) + private const string CloudKey = "cloud"; + private const string ModuleAliasesKey = "moduleAliases"; + private const string AnalyzersKey = "analyzers"; + private const string CacheRootDirectoryKey = "cacheRootDirectory"; + private const string ExperimentalFeaturesEnabledKey = "experimentalFeaturesEnabled"; + + public RootConfiguration( + CloudConfiguration cloud, + ModuleAliasesConfiguration moduleAliases, + AnalyzersConfiguration analyzers, + string? cacheRootDirectory, + ExperimentalFeaturesEnabledConfiguration experimentalFeaturesEnabled, + string? configurationPath, + IEnumerable? diagnosticBuilders) { this.Cloud = cloud; this.ModuleAliases = moduleAliases; this.Analyzers = analyzers; + this.CacheRootDirectory = cacheRootDirectory; + this.ExperimentalFeaturesEnabled = experimentalFeaturesEnabled; this.ConfigurationPath = configurationPath; this.DiagnosticBuilders = diagnosticBuilders?.ToImmutableArray() ?? ImmutableArray.Empty; } public static RootConfiguration Bind(JsonElement element, string? configurationPath = null, IEnumerable? diagnosticBuilders = null) { - var cloud = CloudConfiguration.Bind(element.GetProperty("cloud"), configurationPath); - var moduleAliases = ModuleAliasesConfiguration.Bind(element.GetProperty("moduleAliases"), configurationPath); - var analyzers = new AnalyzersConfiguration(element.GetProperty("analyzers")); + var cloud = CloudConfiguration.Bind(element.GetProperty(CloudKey), configurationPath); + var moduleAliases = ModuleAliasesConfiguration.Bind(element.GetProperty(ModuleAliasesKey), configurationPath); + var analyzers = new AnalyzersConfiguration(element.GetProperty(AnalyzersKey)); + var cacheRootDirectory = element.TryGetProperty(CacheRootDirectoryKey, out var e) ? e.GetString() : default; + var experimentalFeaturesEnabled = new ExperimentalFeaturesEnabledConfiguration(element.GetProperty(ExperimentalFeaturesEnabledKey)); - return new(cloud, moduleAliases, analyzers, configurationPath, diagnosticBuilders); + return new(cloud, moduleAliases, analyzers, cacheRootDirectory, experimentalFeaturesEnabled, configurationPath, diagnosticBuilders); } public CloudConfiguration Cloud { get; } @@ -36,6 +53,10 @@ public static RootConfiguration Bind(JsonElement element, string? configurationP public AnalyzersConfiguration Analyzers { get; } + public string? CacheRootDirectory { get; } + + public ExperimentalFeaturesEnabledConfiguration ExperimentalFeaturesEnabled { get; } + public string? ConfigurationPath { get; } public ImmutableArray DiagnosticBuilders { get; } @@ -49,15 +70,23 @@ public string ToUtf8Json() { writer.WriteStartObject(); - writer.WritePropertyName("cloud"); + writer.WritePropertyName(CloudKey); this.Cloud.WriteTo(writer); - writer.WritePropertyName("moduleAliases"); + writer.WritePropertyName(ModuleAliasesKey); this.ModuleAliases.WriteTo(writer); - writer.WritePropertyName("analyzers"); + writer.WritePropertyName(AnalyzersKey); this.Analyzers.WriteTo(writer); + if (CacheRootDirectory is string cacheRootDir) + { + writer.WriteString(CacheRootDirectoryKey, cacheRootDir); + } + + writer.WritePropertyName(ExperimentalFeaturesEnabledKey); + this.ExperimentalFeaturesEnabled.WriteTo(writer); + writer.WriteEndObject(); } diff --git a/src/Bicep.Core/Configuration/bicepconfig.json b/src/Bicep.Core/Configuration/bicepconfig.json index 3c3037e18df..b7903e99ae3 100644 --- a/src/Bicep.Core/Configuration/bicepconfig.json +++ b/src/Bicep.Core/Configuration/bicepconfig.json @@ -61,5 +61,6 @@ } } } - } + }, + "experimentalFeaturesEnabled": {} } \ No newline at end of file diff --git a/src/Bicep.Core/Features/BicepConfigFeatureProviderSource.cs b/src/Bicep.Core/Features/BicepConfigFeatureProviderSource.cs new file mode 100644 index 00000000000..6879aa050eb --- /dev/null +++ b/src/Bicep.Core/Features/BicepConfigFeatureProviderSource.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using Bicep.Core.Configuration; + +namespace Bicep.Core.Features; + +internal class BicepConfigFeatureProviderSource : IFeatureProviderSource +{ + private readonly RootConfiguration configuration; + + public BicepConfigFeatureProviderSource(RootConfiguration configuration) + { + this.configuration = configuration; + } + + sbyte IFeatureProviderSource.Priority => 1; + public string? CacheRootDirectory => configuration.CacheRootDirectory; + public bool? SymbolicNameCodegenEnabled => configuration.ExperimentalFeaturesEnabled.IsEnabled("symbolicNameCodegen"); + public bool? ImportsEnabled => configuration.ExperimentalFeaturesEnabled.IsEnabled("imports"); + public bool? ResourceTypedParamsAndOutputsEnabled => configuration.ExperimentalFeaturesEnabled.IsEnabled("resourceTypedParamsAndOutputs"); + public bool? SourceMappingEnabled => configuration.ExperimentalFeaturesEnabled.IsEnabled("sourceMapping"); + public bool? ParamsFilesEnabled => configuration.ExperimentalFeaturesEnabled.IsEnabled("paramsFiles"); +} diff --git a/src/Bicep.Core/Features/DefaultsFeatureProvider.cs b/src/Bicep.Core/Features/DefaultsFeatureProvider.cs new file mode 100644 index 00000000000..8f55652c434 --- /dev/null +++ b/src/Bicep.Core/Features/DefaultsFeatureProvider.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System; +using System.IO; + +namespace Bicep.Core.Features; + +internal class DefaultsFeatureProvider : IFeatureProvider +{ + public string CacheRootDirectory => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".bicep"); + + public bool RegistryEnabled => true; + + public bool SymbolicNameCodegenEnabled => false; + + public bool ImportsEnabled => false; + + public bool ResourceTypedParamsAndOutputsEnabled => false; + + public string AssemblyVersion => ThisAssembly.AssemblyFileVersion; + + public bool SourceMappingEnabled => false; + + public bool ParamsFilesEnabled => false; +} diff --git a/src/Bicep.Core/Features/EnvironmentVariablesFeatureProviderSource.cs b/src/Bicep.Core/Features/EnvironmentVariablesFeatureProviderSource.cs new file mode 100644 index 00000000000..0dcd402925d --- /dev/null +++ b/src/Bicep.Core/Features/EnvironmentVariablesFeatureProviderSource.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System; +using System.Diagnostics; + +namespace Bicep.Core.Features; + +internal class EnvironmentVariablesFeatureProviderSource : IFeatureProviderSource +{ + sbyte IFeatureProviderSource.Priority => 0; + + public string? CacheRootDirectory => Environment.GetEnvironmentVariable("BICEP_CACHE_DIRECTORY"); + + public bool? SymbolicNameCodegenEnabled => ReadBooleanEnvVar("BICEP_SYMBOLIC_NAME_CODEGEN_EXPERIMENTAL"); + + public bool? ImportsEnabled => ReadBooleanEnvVar("BICEP_IMPORTS_ENABLED_EXPERIMENTAL"); + + public bool? ResourceTypedParamsAndOutputsEnabled => ReadBooleanEnvVar("BICEP_RESOURCE_TYPED_PARAMS_AND_OUTPUTS_EXPERIMENTAL"); + + public bool? SourceMappingEnabled => ReadBooleanEnvVar("BICEP_SOURCEMAPPING_ENABLED"); + + public bool? paramsFilesEnabledLazy => ReadBooleanEnvVar("BICEP_PARAMS_FILES_ENABLED"); + + private static bool? ReadBooleanEnvVar(string envVar) + { + if (bool.TryParse(Environment.GetEnvironmentVariable(envVar), out bool value)) + { + Trace.WriteLine("Enabling experimental features via environment variables is deprecated. Please migrate to enabling/disabling features via bicepconfig.json"); + return value; + } + + return null; + } +} diff --git a/src/Bicep.Core/Features/FeatureProvider.cs b/src/Bicep.Core/Features/FeatureProvider.cs index ed4a24fb45a..71584166457 100644 --- a/src/Bicep.Core/Features/FeatureProvider.cs +++ b/src/Bicep.Core/Features/FeatureProvider.cs @@ -2,63 +2,80 @@ // Licensed under the MIT License. using System; -using System.IO; +using System.Collections.Generic; +using System.Linq; using System.Threading; namespace Bicep.Core.Features { public class FeatureProvider : IFeatureProvider { - private Lazy cacheRootDirectoryLazy = new(() => GetCacheRootDirectory(Environment.GetEnvironmentVariable("BICEP_CACHE_DIRECTORY")), LazyThreadSafetyMode.PublicationOnly); + private readonly IEnumerable providerChain; + private readonly IFeatureProvider defaultsProvider; + private readonly Lazy cacheRootDirectoryLazy; + private readonly Lazy assemblyVersionLazy; + private readonly Lazy registryEnabledLazy; + private readonly Lazy symbolicNameCodegenEnabledLazy; + private readonly Lazy importsEnabledLazy; + private readonly Lazy resourceTypedParamsAndOutputsEnabledLazy; + private readonly Lazy sourceMappingEnabledLazy; + private readonly Lazy paramsFilesEnabledLazy; + + public FeatureProvider(IFeatureProvider defaultsProvider, IEnumerable sources) + { + this.defaultsProvider = defaultsProvider; + providerChain = sources.OrderBy(s => s.Priority).ToList(); + + cacheRootDirectoryLazy = new(() => LookupFeature(s => s.CacheRootDirectory, d => d.CacheRootDirectory), LazyThreadSafetyMode.PublicationOnly); + assemblyVersionLazy = new(() => LookupFeature(s => s.AssemblyVersion, d => d.AssemblyVersion), LazyThreadSafetyMode.PublicationOnly); + registryEnabledLazy = new(() => LookupFeature(s => s.RegistryEnabled, d => d.RegistryEnabled), LazyThreadSafetyMode.PublicationOnly); + symbolicNameCodegenEnabledLazy = new(() => LookupFeature(s => s.SymbolicNameCodegenEnabled, d => d.SymbolicNameCodegenEnabled), LazyThreadSafetyMode.PublicationOnly); + importsEnabledLazy = new(() => LookupFeature(s => s.ImportsEnabled, d => d.ImportsEnabled), LazyThreadSafetyMode.PublicationOnly); + resourceTypedParamsAndOutputsEnabledLazy = new(() => LookupFeature(s => s.ResourceTypedParamsAndOutputsEnabled, d => d.ResourceTypedParamsAndOutputsEnabled), LazyThreadSafetyMode.PublicationOnly); + sourceMappingEnabledLazy = new(() => LookupFeature(s => s.SourceMappingEnabled, d => d.SourceMappingEnabled), LazyThreadSafetyMode.PublicationOnly); + paramsFilesEnabledLazy = new(() => LookupFeature(s => s.ParamsFilesEnabled, d => d.ParamsFilesEnabled), LazyThreadSafetyMode.PublicationOnly); + } + public string CacheRootDirectory => cacheRootDirectoryLazy.Value; - public bool RegistryEnabled => true; + public bool RegistryEnabled => registryEnabledLazy.Value; - private Lazy symbolicNameCodegenEnabledLazy = new(() => ReadBooleanEnvVar("BICEP_SYMBOLIC_NAME_CODEGEN_EXPERIMENTAL", defaultValue: false), LazyThreadSafetyMode.PublicationOnly); public bool SymbolicNameCodegenEnabled => symbolicNameCodegenEnabledLazy.Value; - private Lazy importsEnabledLazy = new(() => ReadBooleanEnvVar("BICEP_IMPORTS_ENABLED_EXPERIMENTAL", defaultValue: false), LazyThreadSafetyMode.PublicationOnly); public bool ImportsEnabled => importsEnabledLazy.Value; - private Lazy resourceTypedParamsAndOutputsEnabledLazy = new(() => ReadBooleanEnvVar("BICEP_RESOURCE_TYPED_PARAMS_AND_OUTPUTS_EXPERIMENTAL", defaultValue: false), LazyThreadSafetyMode.PublicationOnly); - public bool ResourceTypedParamsAndOutputsEnabled => resourceTypedParamsAndOutputsEnabledLazy.Value; - public string AssemblyVersion => ThisAssembly.AssemblyFileVersion; + public string AssemblyVersion => assemblyVersionLazy.Value; - public bool SourceMappingEnabled => ReadBooleanEnvVar("BICEP_SOURCEMAPPING_ENABLED", defaultValue: false); + public bool SourceMappingEnabled => sourceMappingEnabledLazy.Value; - private Lazy paramsFilesEnabledLazy = new(() => ReadBooleanEnvVar("BICEP_PARAMS_FILES_ENABLED", defaultValue: false), LazyThreadSafetyMode.PublicationOnly); public bool ParamsFilesEnabled => paramsFilesEnabledLazy.Value; - - public static bool TracingEnabled => ReadBooleanEnvVar("BICEP_TRACING_ENABLED", defaultValue: false); - - public static TraceVerbosity TracingVerbosity => ReadEnumEnvvar("BICEP_TRACING_VERBOSITY", TraceVerbosity.Basic); - - private static bool ReadBooleanEnvVar(string envVar, bool defaultValue) - => bool.TryParse(Environment.GetEnvironmentVariable(envVar), out var value) ? value : defaultValue; - private static T ReadEnumEnvvar(string envVar, T defaultValue) where T : struct + private T LookupFeature(Func sourceLookup, Func defaultLookup) { - var str = Environment.GetEnvironmentVariable(envVar); - return Enum.TryParse(str, true, out var value) ? value : defaultValue; - } - - private static string GetDefaultCachePath() - { - string basePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + foreach (var link in providerChain) + { + if (sourceLookup.Invoke(link) is T resolved) + { + return resolved; + } + } - return Path.Combine(basePath, ".bicep"); + return defaultLookup.Invoke(defaultsProvider); } - private static string GetCacheRootDirectory(string? customPath) + private T LookupFeature(Func> sourceLookup, Func defaultLookup) where T : struct { - if (string.IsNullOrWhiteSpace(customPath)) + foreach (var link in providerChain) { - return GetDefaultCachePath(); + if (sourceLookup.Invoke(link) is T resolved) + { + return resolved; + } } - return customPath; + return defaultLookup.Invoke(defaultsProvider); } } } diff --git a/src/Bicep.Core/Features/FeatureProviderFactory.cs b/src/Bicep.Core/Features/FeatureProviderFactory.cs new file mode 100644 index 00000000000..5a42c69cd74 --- /dev/null +++ b/src/Bicep.Core/Features/FeatureProviderFactory.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Bicep.Core.Configuration; + +namespace Bicep.Core.Features; + +public class FeatureProviderFactory : IFeatureProviderFactory +{ + private readonly IConfigurationManager configurationManager; + private readonly IEnumerable sources = ImmutableArray.Create(new EnvironmentVariablesFeatureProviderSource()); + + public FeatureProviderFactory(IConfigurationManager configurationManager) + { + this.configurationManager = configurationManager; + } + + public IFeatureProvider GetFeatureProvider(Uri templateUri) => new FeatureProvider(IFeatureProviderFactory.FeatureDefaults, + sources.Append(new BicepConfigFeatureProviderSource(configurationManager.GetConfiguration(templateUri)))); +} diff --git a/src/Bicep.Core/Features/IFeatureProvider.cs b/src/Bicep.Core/Features/IFeatureProvider.cs index 2a143a20b23..6f0c6ccdf04 100644 --- a/src/Bicep.Core/Features/IFeatureProvider.cs +++ b/src/Bicep.Core/Features/IFeatureProvider.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; + namespace Bicep.Core.Features { public interface IFeatureProvider @@ -20,5 +22,18 @@ public interface IFeatureProvider bool SourceMappingEnabled { get; } bool ParamsFilesEnabled { get; } + + static bool TracingEnabled => ReadBooleanEnvVar("BICEP_TRACING_ENABLED", defaultValue: false); + + static TraceVerbosity TracingVerbosity => ReadEnumEnvvar("BICEP_TRACING_VERBOSITY", TraceVerbosity.Basic); + + private static bool ReadBooleanEnvVar(string envVar, bool defaultValue) + => bool.TryParse(Environment.GetEnvironmentVariable(envVar), out var value) ? value : defaultValue; + + private static T ReadEnumEnvvar(string envVar, T defaultValue) where T : struct + { + var str = Environment.GetEnvironmentVariable(envVar); + return Enum.TryParse(str, true, out var value) ? value : defaultValue; + } } } diff --git a/src/Bicep.Core/Features/IFeatureProviderFactory.cs b/src/Bicep.Core/Features/IFeatureProviderFactory.cs new file mode 100644 index 00000000000..a0363156340 --- /dev/null +++ b/src/Bicep.Core/Features/IFeatureProviderFactory.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System; + +namespace Bicep.Core.Features; + +public interface IFeatureProviderFactory +{ + IFeatureProvider GetFeatureProvider(Uri templateUri); + + static IFeatureProvider FeatureDefaults = new DefaultsFeatureProvider(); + + static IFeatureProviderFactory WithStaticFeatureProvider(IFeatureProvider featureProvider) + => new ConstantFeatureProviderFactory(featureProvider); + + private class ConstantFeatureProviderFactory : IFeatureProviderFactory + { + private readonly IFeatureProvider features; + + internal ConstantFeatureProviderFactory(IFeatureProvider features) + { + this.features = features; + } + + public IFeatureProvider GetFeatureProvider(Uri templateUri) => features; + } +} diff --git a/src/Bicep.Core/Features/IFeatureProviderSource.cs b/src/Bicep.Core/Features/IFeatureProviderSource.cs new file mode 100644 index 00000000000..e44b7088847 --- /dev/null +++ b/src/Bicep.Core/Features/IFeatureProviderSource.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Bicep.Core.Features; + +public interface IFeatureProviderSource +{ + /// + /// A priority ranking for this source. Sources that report numerically lower priority rankings will be queried + /// first (e.g., a source that returns 10 for this property will override a source that reports 11 for this) + /// property. The values 0 and 1 are used by the environment variable and bicepconfig.json sources, so use a + /// negative value to take precedence over the sources built into Bicep.Core. + /// + sbyte Priority { get; } + + string? AssemblyVersion => default; + + string? CacheRootDirectory => default; + + bool? RegistryEnabled => default; + + bool? SymbolicNameCodegenEnabled => default; + + bool? ImportsEnabled => default; + + bool? ResourceTypedParamsAndOutputsEnabled => default; + + bool? SourceMappingEnabled => default; + + bool? ParamsFilesEnabled => default; +} diff --git a/src/Bicep.Core/Registry/DefaultModuleRegistryProvider.cs b/src/Bicep.Core/Registry/DefaultModuleRegistryProvider.cs index 736a2e3dbad..88b985345f3 100644 --- a/src/Bicep.Core/Registry/DefaultModuleRegistryProvider.cs +++ b/src/Bicep.Core/Registry/DefaultModuleRegistryProvider.cs @@ -14,21 +14,22 @@ public class DefaultModuleRegistryProvider : IModuleRegistryProvider private readonly IFileResolver fileResolver; private readonly IContainerRegistryClientFactory clientFactory; private readonly ITemplateSpecRepositoryFactory templateSpecRepositoryFactory; - private readonly IFeatureProvider features; + private readonly IFeatureProviderFactory featureProviderFactory; private readonly IConfigurationManager configurationManager; - public DefaultModuleRegistryProvider(IFileResolver fileResolver, IContainerRegistryClientFactory clientFactory, ITemplateSpecRepositoryFactory templateSpecRepositoryFactory, IFeatureProvider features, IConfigurationManager configurationManager) + public DefaultModuleRegistryProvider(IFileResolver fileResolver, IContainerRegistryClientFactory clientFactory, ITemplateSpecRepositoryFactory templateSpecRepositoryFactory, IFeatureProviderFactory featureProviderFactory, IConfigurationManager configurationManager) { this.fileResolver = fileResolver; this.clientFactory = clientFactory; this.templateSpecRepositoryFactory = templateSpecRepositoryFactory; - this.features = features; + this.featureProviderFactory = featureProviderFactory; this.configurationManager = configurationManager; } public ImmutableArray Registries(Uri templateUri) { var configuration = configurationManager.GetConfiguration(templateUri); + var features = featureProviderFactory.GetFeatureProvider(templateUri); var builder = ImmutableArray.CreateBuilder(); builder.Add(new LocalModuleRegistry(this.fileResolver, templateUri)); if (features.RegistryEnabled) diff --git a/src/Bicep.Core/Rewriters/RewriterHelper.cs b/src/Bicep.Core/Rewriters/RewriterHelper.cs index 06d6bf6a465..1c586d7fe51 100644 --- a/src/Bicep.Core/Rewriters/RewriterHelper.cs +++ b/src/Bicep.Core/Rewriters/RewriterHelper.cs @@ -15,7 +15,7 @@ public static (BicepFile bicepFile, bool hasChanges) Rewrite(Compilation prevCom { // Sometimes bicepFile does not exist in the previous compilation, in which case we fall back on the compilation's entry point model var prevModel = prevCompilation.SourceFileGrouping.FileResultByUri.ContainsKey(bicepFile.FileUri) ? prevCompilation.GetSemanticModel(bicepFile) : prevCompilation.GetEntrypointSemanticModel(); - var semanticModel = new SemanticModel(prevCompilation, bicepFile, prevModel.FileResolver, prevModel.LinterAnalyzer, prevModel.Configuration, prevModel.Features); + var semanticModel = new SemanticModel(prevCompilation, bicepFile, prevModel.FileResolver, prevModel.LinterAnalyzer, prevModel.Configuration, prevModel.Features, prevModel.ApiVersionProvider); var newProgramSyntax = rewriteVisitorBuilder(semanticModel).Rewrite(bicepFile.ProgramSyntax); if (object.ReferenceEquals(bicepFile.ProgramSyntax, newProgramSyntax)) diff --git a/src/Bicep.Core/Semantics/Compilation.cs b/src/Bicep.Core/Semantics/Compilation.cs index f12cb231b84..05c432d069d 100644 --- a/src/Bicep.Core/Semantics/Compilation.cs +++ b/src/Bicep.Core/Semantics/Compilation.cs @@ -18,16 +18,17 @@ public class Compilation { private readonly ImmutableDictionary> lazySemanticModelLookup; private readonly IConfigurationManager configurationManager; - private readonly IFeatureProvider features; + private readonly IFeatureProviderFactory featureProviderFactory; + private readonly IApiVersionProviderFactory apiVersionProviderFactory; private readonly IBicepAnalyzer linterAnalyzer; - public Compilation(IFeatureProvider features, INamespaceProvider namespaceProvider, SourceFileGrouping sourceFileGrouping, IConfigurationManager configurationManager, IApiVersionProvider apiVersionProvider, IBicepAnalyzer linterAnalyzer, ImmutableDictionary? modelLookup = null) + public Compilation(IFeatureProviderFactory featureProviderFactory, INamespaceProvider namespaceProvider, SourceFileGrouping sourceFileGrouping, IConfigurationManager configurationManager, IApiVersionProviderFactory apiVersionProviderFactory, IBicepAnalyzer linterAnalyzer, ImmutableDictionary? modelLookup = null) { - this.features = features; + this.featureProviderFactory = featureProviderFactory; this.SourceFileGrouping = sourceFileGrouping; this.NamespaceProvider = namespaceProvider; this.configurationManager = configurationManager; - this.ApiVersionProvider = apiVersionProvider; + this.apiVersionProviderFactory = apiVersionProviderFactory; this.linterAnalyzer = linterAnalyzer; this.lazySemanticModelLookup = sourceFileGrouping.SourceFiles.ToImmutableDictionary( @@ -43,8 +44,6 @@ public Compilation(IFeatureProvider features, INamespaceProvider namespaceProvid })); } - public IApiVersionProvider ApiVersionProvider { get; } - public SourceFileGrouping SourceFileGrouping { get; } public INamespaceProvider NamespaceProvider { get; } @@ -75,6 +74,7 @@ private T GetSemanticModel(ISourceFile sourceFile) where T : class, ISemantic SourceFileGrouping.FileResolver, linterAnalyzer, configurationManager.GetConfiguration(bicepFile.FileUri), - features); + featureProviderFactory.GetFeatureProvider(bicepFile.FileUri), + apiVersionProviderFactory.GetApiVersionProvider(bicepFile.FileUri)); } } diff --git a/src/Bicep.Core/Semantics/SemanticModel.cs b/src/Bicep.Core/Semantics/SemanticModel.cs index 7fbaece5174..e3b89587674 100644 --- a/src/Bicep.Core/Semantics/SemanticModel.cs +++ b/src/Bicep.Core/Semantics/SemanticModel.cs @@ -13,7 +13,6 @@ using Bicep.Core.Features; using Bicep.Core.FileSystem; using Bicep.Core.Semantics.Metadata; -using Bicep.Core.Semantics.Namespaces; using Bicep.Core.Syntax; using Bicep.Core.Syntax.Visitors; using Bicep.Core.Text; @@ -34,7 +33,7 @@ public class SemanticModel : ISemanticModel private readonly Lazy> declaredResourcesLazy; private readonly Lazy> allDiagnostics; - public SemanticModel(Compilation compilation, BicepFile sourceFile, IFileResolver fileResolver, IBicepAnalyzer linterAnalyzer, RootConfiguration configuration, IFeatureProvider features) + public SemanticModel(Compilation compilation, BicepFile sourceFile, IFileResolver fileResolver, IBicepAnalyzer linterAnalyzer, RootConfiguration configuration, IFeatureProvider features, IApiVersionProvider apiVersionProvider) { Trace.WriteLine($"Building semantic model for {sourceFile.FileUri}"); @@ -42,6 +41,7 @@ public SemanticModel(Compilation compilation, BicepFile sourceFile, IFileResolve SourceFile = sourceFile; Configuration = configuration; Features = features; + ApiVersionProvider = apiVersionProvider; FileResolver = fileResolver; // create this in locked mode by default @@ -130,6 +130,8 @@ public SemanticModel(Compilation compilation, BicepFile sourceFile, IFileResolve public IFeatureProvider Features { get; } + public IApiVersionProvider ApiVersionProvider { get; } + public IBinder Binder { get; } public ISymbolContext SymbolContext { get; } diff --git a/src/Bicep.Decompiler.IntegrationTests/DecompilationTests.cs b/src/Bicep.Decompiler.IntegrationTests/DecompilationTests.cs index 0e120ef4b1d..fc9210e66b0 100644 --- a/src/Bicep.Decompiler.IntegrationTests/DecompilationTests.cs +++ b/src/Bicep.Decompiler.IntegrationTests/DecompilationTests.cs @@ -50,7 +50,7 @@ public void Decompiler_generates_expected_bicep_files_with_diagnostics(EmbeddedF var jsonFile = baselineFolder.EntryFile; var jsonUri = PathHelper.FilePathToFileUrl(jsonFile.OutputFilePath); - var decompiler = new TemplateDecompiler(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, new FileResolver(), BicepTestConstants.RegistryProvider); + var decompiler = new TemplateDecompiler(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, new FileResolver(), BicepTestConstants.RegistryProvider, BicepTestConstants.ApiVersionProviderFactory); var (bicepUri, filesToSave) = decompiler.DecompileFileWithModules(jsonUri, PathHelper.ChangeToBicepExtension(jsonUri)); var result = CompilationHelper.Compile(bicepUri, filesToSave); @@ -94,7 +94,7 @@ public void Decompiler_raises_errors_for_unsupported_features(string resourcePat Action onDecompile = () => { var fileResolver = ReadResourceFile(resourcePath); - var decompiler = new TemplateDecompiler(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.Features, BicepTestConstants.ConfigurationManager)); + var decompiler = new TemplateDecompiler(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.ConfigurationManager), BicepTestConstants.ApiVersionProviderFactory); decompiler.DecompileFileWithModules(new Uri($"file:///{resourcePath}"), new Uri("file:///unused.bicep")); }; @@ -128,7 +128,7 @@ public void Decompiler_handles_strings_with_newlines(string newline, string esca [fileUri] = template, }); ; - var decompiler = new TemplateDecompiler(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.Features, BicepTestConstants.ConfigurationManager)); + var decompiler = new TemplateDecompiler(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.ConfigurationManager), BicepTestConstants.ApiVersionProviderFactory); var (entryPointUri, filesToSave) = decompiler.DecompileFileWithModules(fileUri, PathHelper.ChangeToBicepExtension(fileUri)); // this behavior is actually controlled by newtonsoft's deserializer, but we should assert it anyway to avoid regressions. @@ -179,7 +179,7 @@ public void Decompiler_handles_banned_function_replacement(string expression, st [fileUri] = template, }); - var decompiler = new TemplateDecompiler(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.Features, BicepTestConstants.ConfigurationManager)); + var decompiler = new TemplateDecompiler(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.ConfigurationManager), BicepTestConstants.ApiVersionProviderFactory); var (entryPointUri, filesToSave) = decompiler.DecompileFileWithModules(fileUri, PathHelper.ChangeToBicepExtension(fileUri)); filesToSave[entryPointUri].Should().Contain($"output calculated {type} = ({expectedValue})"); @@ -205,7 +205,7 @@ public void Decompiler_should_not_decompile_bicep_extension() Action sut = () => { - var decompiler = new TemplateDecompiler(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.Features, BicepTestConstants.ConfigurationManager)); + var decompiler = new TemplateDecompiler(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.ConfigurationManager), BicepTestConstants.ApiVersionProviderFactory); decompiler.DecompileFileWithModules(fileUri, PathHelper.ChangeToBicepExtension(fileUri)); }; @@ -267,7 +267,7 @@ public void Decompiler_should_partially_handle_user_defined_functions_with_place [fileUri] = template, }); - var decompiler = new TemplateDecompiler(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.Features, BicepTestConstants.ConfigurationManager)); + var decompiler = new TemplateDecompiler(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.ConfigurationManager), BicepTestConstants.ApiVersionProviderFactory); var (entryPointUri, filesToSave) = decompiler.DecompileFileWithModules(fileUri, PathHelper.ChangeToBicepExtension(fileUri)); filesToSave[entryPointUri].Should().Contain($"? /* TODO: User defined functions are not supported and have not been decompiled */"); @@ -298,7 +298,7 @@ public void Decompiler_should_not_interpret_numbers_with_locale_settings() try { Thread.CurrentThread.CurrentCulture = new CultureInfo("fi-FI"); - var decompiler = new TemplateDecompiler(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.Features, BicepTestConstants.ConfigurationManager)); + var decompiler = new TemplateDecompiler(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), fileResolver, new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.ConfigurationManager), BicepTestConstants.ApiVersionProviderFactory); var (entryPointUri, filesToSave) = decompiler.DecompileFileWithModules(fileUri, PathHelper.ChangeToBicepExtension(fileUri)); filesToSave[entryPointUri].Should().Contain($"var cpu = '0.25'"); diff --git a/src/Bicep.Decompiler/TemplateDecompiler.cs b/src/Bicep.Decompiler/TemplateDecompiler.cs index a9b287b1875..c7eb36f5308 100644 --- a/src/Bicep.Decompiler/TemplateDecompiler.cs +++ b/src/Bicep.Decompiler/TemplateDecompiler.cs @@ -28,18 +28,18 @@ namespace Bicep.Decompiler public class TemplateDecompiler { private readonly INamespaceProvider namespaceProvider; - private readonly IFeatureProvider features; + private readonly IFeatureProviderFactory featureProviderFactory; private readonly IFileResolver fileResolver; private readonly IModuleRegistryProvider registryProvider; - private readonly IApiVersionProvider apiVersionProvider; + private readonly IApiVersionProviderFactory apiVersionProviderFactory; - public TemplateDecompiler(IFeatureProvider features, INamespaceProvider namespaceProvider, IFileResolver fileResolver, IModuleRegistryProvider registryProvider) + public TemplateDecompiler(IFeatureProviderFactory featureProviderFactory, INamespaceProvider namespaceProvider, IFileResolver fileResolver, IModuleRegistryProvider registryProvider, IApiVersionProviderFactory apiVersionProviderFactory) { - this.features = features; + this.featureProviderFactory = featureProviderFactory; this.namespaceProvider = namespaceProvider; this.fileResolver = fileResolver; this.registryProvider = registryProvider; - this.apiVersionProvider = new ApiVersionProvider(features, namespaceProvider); + this.apiVersionProviderFactory = apiVersionProviderFactory; } public (Uri entrypointUri, ImmutableDictionary filesToSave) DecompileFileWithModules(Uri entryJsonUri, Uri entryBicepUri) @@ -131,7 +131,7 @@ private bool RewriteSyntax(Workspace workspace, Uri entryUri, Func x.FileUri).ToImmutableArray(); @@ -151,7 +151,7 @@ private bool RewriteSyntax(Workspace workspace, Uri entryUri, Func options.OnPublishDiagnostics(diagnosticsParams => diagnosticsListener.AddMessage(diagnosticsParams)), new LanguageServer.Server.CreationOptions( NamespaceProvider: BuiltInTestTypes.Create(), - Features: features)); + FeatureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(features))); var client = helper.Client; var outputDirectory = FileHelper.SaveEmbeddedResourcesWithPathPrefix( @@ -78,7 +78,7 @@ public async Task Build_command_should_generate_template_with_symbolic_names_if_ options => options.OnPublishDiagnostics(diagnosticsParams => diagnosticsListener.AddMessage(diagnosticsParams)), new LanguageServer.Server.CreationOptions( NamespaceProvider: BuiltInTestTypes.Create(), - Features: features)); + FeatureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(features))); var client = helper.Client; var outputDirectory = FileHelper.SaveEmbeddedResourcesWithPathPrefix( diff --git a/src/Bicep.LangServer.IntegrationTests/CodeActionTests.cs b/src/Bicep.LangServer.IntegrationTests/CodeActionTests.cs index 3fd075a774c..b2b3f1c288b 100644 --- a/src/Bicep.LangServer.IntegrationTests/CodeActionTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/CodeActionTests.cs @@ -236,7 +236,7 @@ private async Task VerifyCodeActionIsAvailableToSuppressLinterDiagnostics(string var bicepConfigUri = DocumentUri.FromFileSystemPath(bicepConfigFilePath); fileSystemDict[bicepConfigUri.ToUri()] = bicepConfigFileContents; - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(fileSystemDict, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(fileSystemDict, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics(); diagnostics.Should().HaveCount(1); @@ -275,7 +275,7 @@ public async Task VerifyCodeActionIsNotAvailableToSuppressCoreCompilerError() [uri] = bicepFileContents, }; - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics(); diagnostics.Should().HaveCount(2); @@ -337,7 +337,7 @@ public async Task VerifyCodeActionIsAvailableToSuppressCoreCompilerWarning() [uri] = bicepFileContents, }; - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics(); diagnostics.Should().HaveCount(3); diff --git a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs index 612f94ed9dc..3f89da412cb 100644 --- a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Bicep.Core; using Bicep.Core.Extensions; +using Bicep.Core.Features; using Bicep.Core.FileSystem; using Bicep.Core.Parsing; using Bicep.Core.Samples; @@ -83,7 +84,7 @@ public static void ClassInitialize(TestContext testContext) ServerWithImportsEnabled.Initialize( async () => await MultiFileLanguageServerHelper.StartLanguageServer( testContext, - new LanguageServer.Server.CreationOptions(Features: BicepTestConstants.CreateFeatureProvider(testContext, importsEnabled: true)))); + new LanguageServer.Server.CreationOptions(FeatureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(BicepTestConstants.CreateFeatureProvider(testContext, importsEnabled: true))))); ServerWithBuiltInTypes.Initialize( async () => await MultiFileLanguageServerHelper.StartLanguageServer( @@ -162,7 +163,7 @@ public async Task ValidateSnippetCompletionAfterPlaceholderReplacements(Completi { [combinedFileUri] = bicepContentsReplaced, }, combinedFileUri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration); - var compilation = new Compilation(BicepTestConstants.Features, NamespaceProvider, sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics(); var sourceTextWithDiags = OutputHelper.AddDiagsToSourceText(bicepContentsReplaced, "\n", diagnostics, diag => OutputHelper.GetDiagLoggingString(bicepContentsReplaced, outputDirectory, diag)); diff --git a/src/Bicep.LangServer.IntegrationTests/GenerateParamsCommandTests.cs b/src/Bicep.LangServer.IntegrationTests/GenerateParamsCommandTests.cs index 8487f857cad..04b2604d05d 100644 --- a/src/Bicep.LangServer.IntegrationTests/GenerateParamsCommandTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/GenerateParamsCommandTests.cs @@ -13,6 +13,7 @@ using Bicep.Core.UnitTests; using Bicep.LangServer.IntegrationTests.Helpers; using FluentAssertions; +using Bicep.Core.Features; using Bicep.Core.Samples; using Bicep.Core.UnitTests.Assertions; @@ -37,7 +38,7 @@ public async Task GenerateParams_command_should_generate_paramsfile() options => options.OnPublishDiagnostics(diagnosticsParams => diagnosticsListener.AddMessage(diagnosticsParams)), new LanguageServer.Server.CreationOptions( NamespaceProvider: BuiltInTestTypes.Create(), - Features: features)); + FeatureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(features))); var client = helper.Client; var outputDirectory = FileHelper.SaveEmbeddedResourcesWithPathPrefix( diff --git a/src/Bicep.LangServer.IntegrationTests/Helpers/IntegrationTestHelper.cs b/src/Bicep.LangServer.IntegrationTests/Helpers/IntegrationTestHelper.cs index 9f816e1fb3a..e7bb9e361f1 100644 --- a/src/Bicep.LangServer.IntegrationTests/Helpers/IntegrationTestHelper.cs +++ b/src/Bicep.LangServer.IntegrationTests/Helpers/IntegrationTestHelper.cs @@ -35,7 +35,7 @@ public static async Task StartServerWithClientConnectionAsync(T creationOptions = creationOptions with { SnippetsProvider = creationOptions.SnippetsProvider ?? - new SnippetsProvider(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), BicepTestConstants.FileResolver, BicepTestConstants.ConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.ModuleDispatcher), + new SnippetsProvider(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), BicepTestConstants.FileResolver, BicepTestConstants.ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.ModuleDispatcher), FileResolver = creationOptions.FileResolver ?? new InMemoryFileResolver(new Dictionary()) }; diff --git a/src/Bicep.LangServer.IntegrationTests/Helpers/LanguageServerHelper.cs b/src/Bicep.LangServer.IntegrationTests/Helpers/LanguageServerHelper.cs index c1aef0c5432..465bf10793c 100644 --- a/src/Bicep.LangServer.IntegrationTests/Helpers/LanguageServerHelper.cs +++ b/src/Bicep.LangServer.IntegrationTests/Helpers/LanguageServerHelper.cs @@ -24,7 +24,7 @@ namespace Bicep.LangServer.IntegrationTests { public sealed class LanguageServerHelper : IDisposable { - public static readonly ISnippetsProvider SnippetsProvider = new SnippetsProvider(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), BicepTestConstants.FileResolver, BicepTestConstants.ConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.ModuleDispatcher); + public static readonly ISnippetsProvider SnippetsProvider = new SnippetsProvider(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), BicepTestConstants.FileResolver, BicepTestConstants.ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.ModuleDispatcher); public Server Server { get; } public ILanguageClient Client { get; } @@ -49,7 +49,7 @@ public static async Task StartServerWithClientConnectionAs creationOptions ??= new Server.CreationOptions(); creationOptions = creationOptions with { - Features = creationOptions.Features ?? BicepTestConstants.Features, + FeatureProviderFactory = creationOptions.FeatureProviderFactory ?? BicepTestConstants.FeatureProviderFactory, SnippetsProvider = creationOptions.SnippetsProvider ?? SnippetsProvider, FileResolver = creationOptions.FileResolver ?? new InMemoryFileResolver(new Dictionary()), ModuleRestoreScheduler = creationOptions.ModuleRestoreScheduler ?? BicepTestConstants.ModuleRestoreScheduler diff --git a/src/Bicep.LangServer.IntegrationTests/ImportKubernetesManifestTests.cs b/src/Bicep.LangServer.IntegrationTests/ImportKubernetesManifestTests.cs index 1cf40e04777..c991d8089db 100644 --- a/src/Bicep.LangServer.IntegrationTests/ImportKubernetesManifestTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/ImportKubernetesManifestTests.cs @@ -14,6 +14,7 @@ using System.Linq; using FluentAssertions; using Bicep.Core.Extensions; +using Bicep.Core.Features; using Bicep.Core.UnitTests.Baselines; using OmniSharp.Extensions.LanguageServer.Protocol.Window; using Bicep.LangServer.IntegrationTests.Assertions; @@ -51,7 +52,7 @@ public async Task ImportKubernetesManifest_generates_valid_bicep_files_from_kube this.TestContext, options => options .OnTelemetryEvent(telemetryEventsListener.AddMessage), - new LanguageServer.Server.CreationOptions(Features: features)); + new LanguageServer.Server.CreationOptions(FeatureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(features))); var client = helper.Client; var response = await client.SendRequest(new ImportKubernetesManifestRequest(yamlFile.OutputFilePath), default); diff --git a/src/Bicep.LangServer.IntegrationTests/ParamsCompletionTests.cs b/src/Bicep.LangServer.IntegrationTests/ParamsCompletionTests.cs index 3f3995b8e68..e20c9bea2cc 100644 --- a/src/Bicep.LangServer.IntegrationTests/ParamsCompletionTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/ParamsCompletionTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; +using Bicep.Core.Features; using Bicep.Core.FileSystem; using Bicep.Core.UnitTests; using Bicep.Core.UnitTests.Utils; @@ -142,7 +143,7 @@ public async Task Request_for_parameter_identifier_completions_should_return_cor creationOptions: new LanguageServer.Server.CreationOptions( NamespaceProvider: BuiltInTestTypes.Create(), FileResolver: fileResolver, - Features: BicepTestConstants.CreateFeatureProvider(TestContext, paramsFilesEnabled: true))); + FeatureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(BicepTestConstants.CreateFeatureProvider(TestContext, paramsFilesEnabled: true)))); var file = new FileRequestHelper(helper.Client, paramFile); @@ -242,7 +243,7 @@ public async Task Request_for_parameter_allowed_value_completions_should_return_ paramFileTextNoCursor, paramUri, creationOptions: new LanguageServer.Server.CreationOptions(NamespaceProvider: BuiltInTestTypes.Create(), FileResolver: fileResolver, - Features: BicepTestConstants.CreateFeatureProvider(TestContext, paramsFilesEnabled: true))); + FeatureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(BicepTestConstants.CreateFeatureProvider(TestContext, paramsFilesEnabled: true)))); var file = new FileRequestHelper(helper.Client, paramFile); @@ -290,7 +291,7 @@ public async Task Request_for_using_declaration_path_completions_should_return_c paramFileTextNoCursor, paramUri, creationOptions: new LanguageServer.Server.CreationOptions(NamespaceProvider: BuiltInTestTypes.Create(), FileResolver: fileResolver, - Features: BicepTestConstants.CreateFeatureProvider(TestContext, paramsFilesEnabled: true))); + FeatureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(BicepTestConstants.CreateFeatureProvider(TestContext, paramsFilesEnabled: true)))); var file = new FileRequestHelper(helper.Client, paramFile); @@ -340,7 +341,7 @@ public async Task Request_for_using_declaration_path_completions_should_return_c paramFileTextNoCursor, paramUri, creationOptions: new LanguageServer.Server.CreationOptions(NamespaceProvider: BuiltInTestTypes.Create(), FileResolver: fileResolver, - Features: BicepTestConstants.CreateFeatureProvider(TestContext, paramsFilesEnabled: true))); + FeatureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(BicepTestConstants.CreateFeatureProvider(TestContext, paramsFilesEnabled: true)))); var file = new FileRequestHelper(helper.Client, paramFile); diff --git a/src/Bicep.LangServer.IntegrationTests/SemanticTokenTests.cs b/src/Bicep.LangServer.IntegrationTests/SemanticTokenTests.cs index 38257987bc9..b2700cb1797 100644 --- a/src/Bicep.LangServer.IntegrationTests/SemanticTokenTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/SemanticTokenTests.cs @@ -20,6 +20,7 @@ using Bicep.LangServer.IntegrationTests.Helpers; using System; using System.Collections.Immutable; +using Bicep.Core.Features; using Bicep.Core.FileSystem; using Bicep.Core.UnitTests.Utils; using Bicep.Core.UnitTests; @@ -106,7 +107,7 @@ public async Task Correct_semantic_tokens_are_returned_for_params_file(string pa creationOptions: new LanguageServer.Server.CreationOptions( NamespaceProvider: BuiltInTestTypes.Create(), FileResolver: fileResolver, - Features: BicepTestConstants.CreateFeatureProvider(TestContext, paramsFilesEnabled: true))); + FeatureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(BicepTestConstants.CreateFeatureProvider(TestContext, paramsFilesEnabled: true)))); var semanticTokens = await helper.Client.TextDocument.RequestSemanticTokens(new SemanticTokensParams { diff --git a/src/Bicep.LangServer.IntegrationTests/SnippetTemplatesTests.cs b/src/Bicep.LangServer.IntegrationTests/SnippetTemplatesTests.cs index bdbf8536f0b..8dc21fe4a88 100644 --- a/src/Bicep.LangServer.IntegrationTests/SnippetTemplatesTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/SnippetTemplatesTests.cs @@ -59,7 +59,7 @@ public void VerifySnippetTemplatesAreErrorFree(CompletionData completionData) return; } - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var semanticModel = compilation.GetEntrypointSemanticModel(); if (semanticModel.HasErrors()) diff --git a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs index 1908e029f25..b38c3391310 100644 --- a/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/TelemetryTests.cs @@ -160,7 +160,7 @@ public async Task VerifyDisableNextLineCodeActionInvocationFiresTelemetryEvent() [uri] = bicepFileContents, }; - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics(); var telemetryEventsListener = new MultipleMessageListener(); diff --git a/src/Bicep.LangServer.UnitTests/BicepCompilationManagerHelper.cs b/src/Bicep.LangServer.UnitTests/BicepCompilationManagerHelper.cs index 45958294c2d..eb518e76d65 100644 --- a/src/Bicep.LangServer.UnitTests/BicepCompilationManagerHelper.cs +++ b/src/Bicep.LangServer.UnitTests/BicepCompilationManagerHelper.cs @@ -52,10 +52,10 @@ public static BicepParamsCompilationManager CreateParamsCompilationManager(){ var document = CreateMockDocument(p => receivedParams = p); var server = CreateMockServer(document); var configManager = new ConfigurationManager(new IOFileSystem()); - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(FileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.Features, configManager), configManager); - var provider = new BicepCompilationProvider(BicepTestConstants.Features, TestTypeHelper.CreateWithAzTypes(), FileResolver, dispatcher, BicepTestConstants.ApiVersionProvider, configManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(FileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, configManager), configManager); + var provider = new BicepCompilationProvider(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateWithAzTypes(), FileResolver, dispatcher, BicepTestConstants.ApiVersionProviderFactory, configManager); - return new BicepParamsCompilationManager(server.Object, provider, configManager, BicepTestConstants.FileResolver, dispatcher, new Workspace(), BicepTestConstants.Features, BicepTestConstants.ApiVersionProvider, BicepTestConstants.NamespaceProvider); + return new BicepParamsCompilationManager(server.Object, provider, configManager, BicepTestConstants.FileResolver, dispatcher, new Workspace(), BicepTestConstants.FeatureProviderFactory, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.NamespaceProvider); } public static Mock CreateMockDocument(Action callback) @@ -89,7 +89,7 @@ public static Mock CreateMockServer(Mock CreateMockScheduler() diff --git a/src/Bicep.LangServer.UnitTests/BicepCompilationManagerTests.cs b/src/Bicep.LangServer.UnitTests/BicepCompilationManagerTests.cs index ae40ee92e53..6123e0df7f0 100644 --- a/src/Bicep.LangServer.UnitTests/BicepCompilationManagerTests.cs +++ b/src/Bicep.LangServer.UnitTests/BicepCompilationManagerTests.cs @@ -515,7 +515,7 @@ public void SemanticModels_should_only_be_reloaded_on_sourcefile_or_dependent_so var server = BicepCompilationManagerHelper.CreateMockServer(document); var fileResolver = new InMemoryFileResolver(fileDict); - var compilationProvider = new BicepCompilationProvider(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), fileResolver, new ModuleDispatcher(new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.Features, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.ApiVersionProvider, BicepTestConstants.BuiltInOnlyConfigurationManager); + var compilationProvider = new BicepCompilationProvider(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), fileResolver, new ModuleDispatcher(new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.BuiltInOnlyConfigurationManager); var compilationManager = new BicepCompilationManager(server.Object, compilationProvider, new Workspace(), fileResolver, BicepCompilationManagerHelper.CreateMockScheduler().Object, configurationManager, BicepTestConstants.CreateMockTelemetryProvider().Object, linterRulesProvider); @@ -752,7 +752,7 @@ public void GetTelemetryAboutSourceFiles_ShouldReturnTelemetryEvent() var bicepFile = SourceFileFactory.CreateBicepFile(mainUri, bicepFileContents); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(bicepFileContents, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(bicepFileContents, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics().ToDiagnostics(bicepFile.LineStarts); var compilationManager = CreateBicepCompilationManager(); diff --git a/src/Bicep.LangServer.UnitTests/BicepCompilationProviderTests.cs b/src/Bicep.LangServer.UnitTests/BicepCompilationProviderTests.cs index 13124f4e4c5..0e2bf4d701f 100644 --- a/src/Bicep.LangServer.UnitTests/BicepCompilationProviderTests.cs +++ b/src/Bicep.LangServer.UnitTests/BicepCompilationProviderTests.cs @@ -35,9 +35,9 @@ public void Create_ShouldReturnValidCompilation() var fileUri = DocumentUri.Parse($"/{DataSets.Parameters_LF.Name}.bicep"); var fileResolver = CreateFileResolver(fileUri.ToUri(), DataSets.Parameters_LF.Bicep); var configurationManager = IConfigurationManager.WithStaticConfiguration(BicepTestConstants.BuiltInConfigurationWithAllAnalyzersDisabled); - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.Features, configurationManager), configurationManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, configurationManager), configurationManager); - var provider = new BicepCompilationProvider(BicepTestConstants.Features, TestTypeHelper.CreateWithAzTypes(), fileResolver, dispatcher, BicepTestConstants.ApiVersionProvider, configurationManager); + var provider = new BicepCompilationProvider(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateWithAzTypes(), fileResolver, dispatcher, BicepTestConstants.ApiVersionProviderFactory, configurationManager); var sourceFile = SourceFileFactory.CreateSourceFile(fileUri.ToUri(), DataSets.Parameters_LF.Bicep); var workspace = new Workspace(); diff --git a/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs b/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs index 5715c4d767f..eadfab8f00e 100644 --- a/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs +++ b/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs @@ -29,13 +29,13 @@ public class BicepCompletionProviderTests { private static readonly MockRepository Repository = new MockRepository(MockBehavior.Strict); private static readonly ILanguageServerFacade Server = Repository.Create().Object; - private static readonly SnippetsProvider snippetsProvider = new(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), BicepTestConstants.FileResolver, BicepTestConstants.ConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.ModuleDispatcher); + private static readonly SnippetsProvider snippetsProvider = new(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), BicepTestConstants.FileResolver, BicepTestConstants.ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.ModuleDispatcher); [TestMethod] public void DeclarationContextShouldReturnKeywordCompletions() { var grouping = SourceFileGroupingFactory.CreateFromText(string.Empty, BicepTestConstants.FileResolver); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); compilation.GetEntrypointSemanticModel().GetAllDiagnostics().Should().BeEmpty(); BicepCompletionProvider completionProvider = new(BicepTestConstants.FileResolver, snippetsProvider, new TelemetryProvider(Server), compilation.NamespaceProvider); @@ -124,7 +124,7 @@ param p string output o int = 42 ", BicepTestConstants.FileResolver); var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Value.Span.Position; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var context = BicepCompletionContext.Create(BicepTestConstants.Features, compilation, offset); BicepCompletionProvider completionProvider = new(BicepTestConstants.FileResolver, snippetsProvider, new TelemetryProvider(Server), compilation.NamespaceProvider); @@ -161,7 +161,7 @@ param p string public void CompletionsForOneLinerParameterDefaultValueShouldIncludeFunctionsValidInDefaultValues() { var grouping = SourceFileGroupingFactory.CreateFromText(@"param p string = ", BicepTestConstants.FileResolver); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var offset = ((ParameterDefaultValueSyntax)grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Modifier!).DefaultValue.Span.Position; @@ -197,7 +197,7 @@ param concat string ", BicepTestConstants.FileResolver); var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Value.Span.Position; - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var context = BicepCompletionContext.Create(BicepTestConstants.Features, compilation, offset); BicepCompletionProvider completionProvider = new(BicepTestConstants.FileResolver, snippetsProvider, new TelemetryProvider(Server), compilation.NamespaceProvider); @@ -240,7 +240,7 @@ param concat string public void OutputTypeContextShouldReturnDeclarationTypeCompletions() { var grouping = SourceFileGroupingFactory.CreateFromText("output test ", BicepTestConstants.FileResolver); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Type.Span.Position; @@ -257,7 +257,7 @@ public void OutputTypeContextShouldReturnDeclarationTypeCompletions() public void ParameterTypeContextShouldReturnDeclarationTypeCompletions() { var grouping = SourceFileGroupingFactory.CreateFromText("param foo ", BicepTestConstants.FileResolver); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Type.Span.Position; @@ -304,7 +304,7 @@ public void ParameterTypeContextShouldReturnDeclarationTypeCompletions() public void VerifyParameterTypeCompletionWithPrecedingComment() { var grouping = SourceFileGroupingFactory.CreateFromText("/*test*/param foo ", BicepTestConstants.FileResolver); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType().Single().Type.Span.Position; @@ -362,7 +362,7 @@ public void VerifyParameterTypeCompletionWithPrecedingComment() public void CommentShouldNotGiveAnyCompletions(string codeFragment) { var grouping = SourceFileGroupingFactory.CreateFromText(codeFragment, BicepTestConstants.FileResolver); - var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), grouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); var offset = codeFragment.IndexOf('|'); diff --git a/src/Bicep.LangServer.UnitTests/Completions/BicepCompletionContextTests.cs b/src/Bicep.LangServer.UnitTests/Completions/BicepCompletionContextTests.cs index 16d97699657..870dbb52e0c 100644 --- a/src/Bicep.LangServer.UnitTests/Completions/BicepCompletionContextTests.cs +++ b/src/Bicep.LangServer.UnitTests/Completions/BicepCompletionContextTests.cs @@ -17,7 +17,7 @@ public class BicepCompletionContextTests public void ZeroMatchingNodes_Create_ShouldThrow() { const string text = "var foo = 42"; - var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateFromText(text, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateFromText(text, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer); Action fail = () => BicepCompletionContext.Create(BicepTestConstants.Features, compilation, text.Length + 2); fail.Should().Throw().WithMessage("The specified offset 14 is outside the span of the specified ProgramSyntax node."); diff --git a/src/Bicep.LangServer.UnitTests/Handlers/BicepBuildCommandHandlerTests.cs b/src/Bicep.LangServer.UnitTests/Handlers/BicepBuildCommandHandlerTests.cs index 23b2358d684..849a7ae0532 100644 --- a/src/Bicep.LangServer.UnitTests/Handlers/BicepBuildCommandHandlerTests.cs +++ b/src/Bicep.LangServer.UnitTests/Handlers/BicepBuildCommandHandlerTests.cs @@ -44,7 +44,7 @@ public class BicepBuildCommandHandlerTests public void Handle_WithInvalidPath_ShouldThrowArgumentException(string path) { ICompilationManager bicepCompilationManager = Repository.Create().Object; - BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Serializer, BicepTestConstants.Features, BicepTestConstants.EmitterSettings, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProvider, configurationManager); + BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Serializer, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProviderFactory, configurationManager); Action sut = () => bicepBuildCommandHandler.Handle(path, CancellationToken.None); @@ -62,7 +62,7 @@ public async Task Handle_WithNullContext_ShouldCreateCompilation() DocumentUri documentUri = DocumentUri.FromFileSystemPath(bicepFilePath); // Do not upsert compilation. This will cause CompilationContext to be null BicepCompilationManager bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, bicepFileContents, upsertCompilation: false); - BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Serializer, BicepTestConstants.Features, BicepTestConstants.EmitterSettings, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProvider, configurationManager); + BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Serializer, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProviderFactory, configurationManager); string expected = await bicepBuildCommandHandler.Handle(bicepFilePath, CancellationToken.None); expected.Should().Be(@"Bicep build succeeded. Created ARM template file: input.json"); @@ -85,7 +85,7 @@ public async Task Handle_WithValidPath_AndOnlyWarningsAndInfoInInputFile_Returns DocumentUri documentUri = DocumentUri.From(bicepFileUri); BicepCompilationManager bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, bicepFileContents, true); - BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Repository.Create().Object, BicepTestConstants.Features, BicepTestConstants.EmitterSettings, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProvider, configurationManager); + BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Repository.Create().Object, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProviderFactory, configurationManager); string expected = await bicepBuildCommandHandler.Handle(bicepFilePath, CancellationToken.None); expected.Should().Be(@"Bicep build succeeded. Created ARM template file: input.json"); @@ -111,7 +111,7 @@ public async Task Handle_WithValidPath_AndErrorsAndWarningsInInputFile_ReturnsBu targetScope = true param accountName string = 'testAccount' ", true); - BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Repository.Create().Object, BicepTestConstants.Features, BicepTestConstants.EmitterSettings, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProvider, configurationManager); + BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Repository.Create().Object, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProviderFactory, configurationManager); string expected = await bicepBuildCommandHandler.Handle(documentUri.Path, CancellationToken.None); expected.Should().BeEquivalentToIgnoringNewlines(@"Bicep build failed. Please fix below errors: @@ -145,7 +145,7 @@ public async Task Handle_WhenCompiledFileAlreadyExists_ReturnsBuildFailedMessage FileHelper.SaveResultFile(TestContext, "input.json", string.Empty, outputPath); DocumentUri documentUri = DocumentUri.FromFileSystemPath(bicepFilePath); BicepCompilationManager bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, string.Empty, true); - BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Repository.Create().Object, BicepTestConstants.Features, BicepTestConstants.EmitterSettings, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProvider, configurationManager); + BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Repository.Create().Object, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProviderFactory, configurationManager); string expected = await bicepBuildCommandHandler.Handle(bicepFilePath, CancellationToken.None); expected.Should().Be(@"Bicep build failed. The output file ""input.json"" already exists and was not generated by Bicep. If overwriting the file is intended, delete it manually and retry the build command."); @@ -159,7 +159,7 @@ public async Task Handle_WhenCompiledFileAlreadyExistsAndIsMalformed_ReturnsBuil FileHelper.SaveResultFile(TestContext, "input.json", "invalid json", outputPath); DocumentUri documentUri = DocumentUri.FromFileSystemPath(bicepFilePath); BicepCompilationManager bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, string.Empty, true); - BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Repository.Create().Object, BicepTestConstants.Features, BicepTestConstants.EmitterSettings, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProvider, configurationManager); + BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Repository.Create().Object, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProviderFactory, configurationManager); string expected = await bicepBuildCommandHandler.Handle(bicepFilePath, CancellationToken.None); expected.Should().Be(@"Bicep build failed. The output file ""input.json"" already exists and was not generated by Bicep. If overwriting the file is intended, delete it manually and retry the build command."); @@ -176,7 +176,7 @@ public async Task Handle_WithValidPath_AndNoErrorsInInputFile_ReturnsBuildSuccee "); DocumentUri documentUri = DocumentUri.FromFileSystemPath(bicepFilePath); BicepCompilationManager bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, string.Empty, true); - BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Repository.Create().Object, BicepTestConstants.Features, BicepTestConstants.EmitterSettings, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProvider, configurationManager); + BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Repository.Create().Object, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProviderFactory, configurationManager); string expected = await bicepBuildCommandHandler.Handle(bicepFilePath, CancellationToken.None); expected.Should().Be(@"Bicep build succeeded. Created ARM template file: input.json"); @@ -189,7 +189,7 @@ public async Task Handle_WithValidPath_AndNoErrorsInInputFile_ReturnsBuildSuccee public void TemplateContainsBicepGeneratorMetadata_WithInvalidInput_ReturnsFalse(string template) { ICompilationManager bicepCompilationManager = Repository.Create().Object; - BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Serializer, BicepTestConstants.Features, BicepTestConstants.EmitterSettings, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProvider, configurationManager); + BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Serializer, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProviderFactory, configurationManager); bool actual = bicepBuildCommandHandler.TemplateContainsBicepGeneratorMetadata(template); @@ -200,7 +200,7 @@ public void TemplateContainsBicepGeneratorMetadata_WithInvalidInput_ReturnsFalse public void TemplateContainsBicepGeneratorMetadata_WithBicepGeneratorMetadataInInput_ReturnsTrue() { ICompilationManager bicepCompilationManager = Repository.Create().Object; - BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Serializer, BicepTestConstants.Features, BicepTestConstants.EmitterSettings, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProvider, configurationManager); + BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Serializer, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProviderFactory, configurationManager); string template = @"{ ""$schema"": ""https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#"", ""contentVersion"": ""1.0.0.0"", @@ -224,7 +224,7 @@ public void TemplateContainsBicepGeneratorMetadata_WithBicepGeneratorMetadataInI public void TemplateContainsBicepGeneratorMetadata_WithoutBicepGeneratorMetadataInInput_ReturnsFalse() { ICompilationManager bicepCompilationManager = Repository.Create().Object; - BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Serializer, BicepTestConstants.Features, BicepTestConstants.EmitterSettings, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProvider, configurationManager); + BicepBuildCommandHandler bicepBuildCommandHandler = new BicepBuildCommandHandler(bicepCompilationManager, Serializer, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, FileResolver, ModuleDispatcher, BicepTestConstants.ApiVersionProviderFactory, configurationManager); string template = @"{ ""$schema"": ""https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#"", ""contentVersion"": ""1.0.0.0"", diff --git a/src/Bicep.LangServer.UnitTests/Handlers/BicepDeploymentScopeRequestHandlerTests.cs b/src/Bicep.LangServer.UnitTests/Handlers/BicepDeploymentScopeRequestHandlerTests.cs index 92c3a03d16e..c0cd191f781 100644 --- a/src/Bicep.LangServer.UnitTests/Handlers/BicepDeploymentScopeRequestHandlerTests.cs +++ b/src/Bicep.LangServer.UnitTests/Handlers/BicepDeploymentScopeRequestHandlerTests.cs @@ -50,16 +50,15 @@ public async Task Handle_WithInvalidInputFile_ReturnsBicepDeploymentScopeRespons BicepCompilationManager bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, bicepFileContents, true); BicepDeploymentScopeRequestHandler bicepDeploymentScopeRequestHandler = new BicepDeploymentScopeRequestHandler( - BicepTestConstants.EmitterSettings, bicepCompilationManager, configurationManager, new DeploymentFileCompilationCache(), - BicepTestConstants.Features, + BicepTestConstants.FeatureProviderFactory, FileResolver, ModuleDispatcher, BicepTestConstants.NamespaceProvider, Serializer, - BicepTestConstants.ApiVersionProvider); + BicepTestConstants.ApiVersionProviderFactory); var textDocumentIdentifier = new TextDocumentIdentifier(documentUri); BicepDeploymentScopeParams bicepDeploymentScopeParams = new BicepDeploymentScopeParams(textDocumentIdentifier); @@ -85,16 +84,15 @@ public async Task Handle_WithInvalidConfigurationFile_ReturnsBicepDeploymentScop BicepCompilationManager bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, bicepFileContents, true); BicepDeploymentScopeRequestHandler bicepDeploymentScopeRequestHandler = new BicepDeploymentScopeRequestHandler( - BicepTestConstants.EmitterSettings, bicepCompilationManager, configurationManager, new DeploymentFileCompilationCache(), - BicepTestConstants.Features, + BicepTestConstants.FeatureProviderFactory, FileResolver, ModuleDispatcher, BicepTestConstants.NamespaceProvider, Serializer, - BicepTestConstants.ApiVersionProvider); + BicepTestConstants.ApiVersionProviderFactory); var textDocumentIdentifier = new TextDocumentIdentifier(documentUri); BicepDeploymentScopeParams bicepDeploymentScopeParams = new BicepDeploymentScopeParams(textDocumentIdentifier); @@ -118,16 +116,15 @@ public async Task Handle_WithValidInputFile_ReturnsBicepDeploymentScopeResponse( BicepCompilationManager bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, bicepFileContents, true); BicepDeploymentScopeRequestHandler bicepDeploymentScopeRequestHandler = new BicepDeploymentScopeRequestHandler( - BicepTestConstants.EmitterSettings, bicepCompilationManager, configurationManager, new DeploymentFileCompilationCache(), - BicepTestConstants.Features, + BicepTestConstants.FeatureProviderFactory, FileResolver, ModuleDispatcher, BicepTestConstants.NamespaceProvider, Serializer, - BicepTestConstants.ApiVersionProvider); + BicepTestConstants.ApiVersionProviderFactory); var textDocumentIdentifier = new TextDocumentIdentifier(documentUri); BicepDeploymentScopeParams bicepDeploymentScopeParams = new BicepDeploymentScopeParams(textDocumentIdentifier); @@ -175,16 +172,15 @@ public async Task Handle_WithValidInputFile_VerifyDeploymentScope(string scope, BicepCompilationManager bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, bicepFileContents, true); BicepDeploymentScopeRequestHandler bicepDeploymentScopeRequestHandler = new BicepDeploymentScopeRequestHandler( - BicepTestConstants.EmitterSettings, bicepCompilationManager, configurationManager, new DeploymentFileCompilationCache(), - BicepTestConstants.Features, + BicepTestConstants.FeatureProviderFactory, FileResolver, ModuleDispatcher, BicepTestConstants.NamespaceProvider, Serializer, - BicepTestConstants.ApiVersionProvider); + BicepTestConstants.ApiVersionProviderFactory); var textDocumentIdentifier = new TextDocumentIdentifier(documentUri); BicepDeploymentScopeParams bicepDeploymentScopeParams = new BicepDeploymentScopeParams(textDocumentIdentifier); @@ -207,16 +203,15 @@ public async Task Handle_WithValidInputFile_VerifyCompilationEntryIsAddedToDeplo var deploymentFileCompilationCache = new DeploymentFileCompilationCache(); BicepDeploymentScopeRequestHandler bicepDeploymentScopeRequestHandler = new BicepDeploymentScopeRequestHandler( - BicepTestConstants.EmitterSettings, bicepCompilationManager, configurationManager, deploymentFileCompilationCache, - BicepTestConstants.Features, + BicepTestConstants.FeatureProviderFactory, FileResolver, ModuleDispatcher, BicepTestConstants.NamespaceProvider, Serializer, - BicepTestConstants.ApiVersionProvider); + BicepTestConstants.ApiVersionProviderFactory); var textDocumentIdentifier = new TextDocumentIdentifier(documentUri); BicepDeploymentScopeParams bicepDeploymentScopeParams = new BicepDeploymentScopeParams(textDocumentIdentifier); diff --git a/src/Bicep.LangServer.UnitTests/Snippets/SnippetsProviderTests.cs b/src/Bicep.LangServer.UnitTests/Snippets/SnippetsProviderTests.cs index 690cb5b36ec..2b9cc63b68d 100644 --- a/src/Bicep.LangServer.UnitTests/Snippets/SnippetsProviderTests.cs +++ b/src/Bicep.LangServer.UnitTests/Snippets/SnippetsProviderTests.cs @@ -19,7 +19,7 @@ namespace Bicep.LangServer.UnitTests.Snippets [TestClass] public class SnippetsProviderTests { - private readonly SnippetsProvider snippetsProvider = new(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, BicepTestConstants.FileResolver, BicepTestConstants.ConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.ModuleDispatcher); + private readonly SnippetsProvider snippetsProvider = new(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, BicepTestConstants.FileResolver, BicepTestConstants.ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.ModuleDispatcher); private readonly NamespaceType azNamespaceType = BicepTestConstants.NamespaceProvider.TryGetNamespace("az", "az", ResourceScope.ResourceGroup, BicepTestConstants.Features)!; [TestMethod] diff --git a/src/Bicep.LangServer/BicepParamsCompilationManager.cs b/src/Bicep.LangServer/BicepParamsCompilationManager.cs index 7edabb7113a..aacb2cc8497 100644 --- a/src/Bicep.LangServer/BicepParamsCompilationManager.cs +++ b/src/Bicep.LangServer/BicepParamsCompilationManager.cs @@ -31,11 +31,11 @@ public class BicepParamsCompilationManager : IParamsCompilationManager private readonly IFileResolver fileResolver; private readonly IModuleDispatcher moduleDispatcher; private readonly IWorkspace workspace; - private readonly IFeatureProvider features; - private readonly IApiVersionProvider apiVersionProvider; + private readonly IFeatureProviderFactory featureProviderFactory; + private readonly IApiVersionProviderFactory apiVersionProviderFactory; private readonly INamespaceProvider namespaceProvider; private readonly ConcurrentDictionary activeContexts = new ConcurrentDictionary(); - public BicepParamsCompilationManager(ILanguageServerFacade server, ICompilationProvider bicepCompilationContextProvider, IConfigurationManager bicepConfigurationManager, IFileResolver fileResolver, IModuleDispatcher moduleDispatcher, IWorkspace workspace, IFeatureProvider features, IApiVersionProvider apiVersionProvider, INamespaceProvider namespaceProvider) + public BicepParamsCompilationManager(ILanguageServerFacade server, ICompilationProvider bicepCompilationContextProvider, IConfigurationManager bicepConfigurationManager, IFileResolver fileResolver, IModuleDispatcher moduleDispatcher, IWorkspace workspace, IFeatureProviderFactory featureProviderFactory, IApiVersionProviderFactory apiVersionProviderFactory, INamespaceProvider namespaceProvider) { this.server = server; this.bicepCompilationContextProvider = bicepCompilationContextProvider; @@ -43,8 +43,8 @@ public BicepParamsCompilationManager(ILanguageServerFacade server, ICompilationP this.fileResolver = fileResolver; this.moduleDispatcher = moduleDispatcher; this.workspace = workspace; - this.features = features; - this.apiVersionProvider = apiVersionProvider; + this.featureProviderFactory = featureProviderFactory; + this.apiVersionProviderFactory = apiVersionProviderFactory; this.namespaceProvider = namespaceProvider; } @@ -64,11 +64,10 @@ public void UpsertCompilation(DocumentUri uri, int? version, string text, string var sourceFileGrouping = SourceFileGroupingBuilder.Build(this.fileResolver, this.moduleDispatcher, this.workspace, inputUri); - var semanticModel = new ParamsSemanticModel(sourceFileGrouping, bicepConfigurationManager.GetConfiguration(inputUri), features, file => { + var semanticModel = new ParamsSemanticModel(sourceFileGrouping, bicepConfigurationManager.GetConfiguration(inputUri), featureProviderFactory.GetFeatureProvider(inputUri), file => { var compilationGrouping = new SourceFileGrouping(fileResolver, file.FileUri, sourceFileGrouping.FileResultByUri, sourceFileGrouping.UriResultByModule, sourceFileGrouping.SourceFileParentLookup); - - return new Compilation(features, namespaceProvider, compilationGrouping, bicepConfigurationManager, apiVersionProvider, new LinterAnalyzer()); + return new Compilation(featureProviderFactory, namespaceProvider, compilationGrouping, bicepConfigurationManager, apiVersionProviderFactory, new LinterAnalyzer()); }); var context = this.activeContexts.AddOrUpdate( diff --git a/src/Bicep.LangServer/Handlers/BicepBuildCommandHandler.cs b/src/Bicep.LangServer/Handlers/BicepBuildCommandHandler.cs index c50a035b76e..3438f48b88e 100644 --- a/src/Bicep.LangServer/Handlers/BicepBuildCommandHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepBuildCommandHandler.cs @@ -34,25 +34,23 @@ namespace Bicep.LanguageServer.Handlers public class BicepBuildCommandHandler : ExecuteTypedResponseCommandHandlerBase { private readonly ICompilationManager compilationManager; - private readonly EmitterSettings emitterSettings; - private readonly IFeatureProvider features; + private readonly IFeatureProviderFactory featureProviderFactory; private readonly IFileResolver fileResolver; private readonly IModuleDispatcher moduleDispatcher; private readonly INamespaceProvider namespaceProvider; private readonly IConfigurationManager configurationManager; - private readonly IApiVersionProvider apiVersionProvider; + private readonly IApiVersionProviderFactory apiVersionProviderFactory; - public BicepBuildCommandHandler(ICompilationManager compilationManager, ISerializer serializer, IFeatureProvider features, EmitterSettings emitterSettings, INamespaceProvider namespaceProvider, IFileResolver fileResolver, IModuleDispatcher moduleDispatcher, IApiVersionProvider apiVersionProvider, IConfigurationManager configurationManager) + public BicepBuildCommandHandler(ICompilationManager compilationManager, ISerializer serializer, IFeatureProviderFactory featureProviderFactory, INamespaceProvider namespaceProvider, IFileResolver fileResolver, IModuleDispatcher moduleDispatcher, IApiVersionProviderFactory apiVersionProviderFactory, IConfigurationManager configurationManager) : base(LangServerConstants.BuildCommand, serializer) { this.compilationManager = compilationManager; - this.emitterSettings = emitterSettings; - this.features = features; + this.featureProviderFactory = featureProviderFactory; this.namespaceProvider = namespaceProvider; this.fileResolver = fileResolver; this.moduleDispatcher = moduleDispatcher; this.configurationManager = configurationManager; - this.apiVersionProvider = apiVersionProvider; + this.apiVersionProviderFactory = apiVersionProviderFactory; } public override Task Handle(string bicepFilePath, CancellationToken cancellationToken) @@ -88,7 +86,7 @@ private string GenerateCompiledFileAndReturnBuildOutputMessage(string bicepFileP if (context is null) { SourceFileGrouping sourceFileGrouping = SourceFileGroupingBuilder.Build(this.fileResolver, this.moduleDispatcher, new Workspace(), fileUri); - compilation = new Compilation(features, namespaceProvider, sourceFileGrouping, configurationManager, apiVersionProvider, new LinterAnalyzer()); + compilation = new Compilation(featureProviderFactory, namespaceProvider, sourceFileGrouping, configurationManager, apiVersionProviderFactory, new LinterAnalyzer()); } else { @@ -104,7 +102,8 @@ private string GenerateCompiledFileAndReturnBuildOutputMessage(string bicepFileP } using var fileStream = new FileStream(compiledFilePath, FileMode.Create, FileAccess.ReadWrite); - var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), emitterSettings); + var model = compilation.GetEntrypointSemanticModel(); + var emitter = new TemplateEmitter(model, new EmitterSettings(model.Features)); EmitResult result = emitter.Emit(fileStream); return "Bicep build succeeded. Created ARM template file: " + compiledFile; diff --git a/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs b/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs index 6d4e074284c..1f3b36c5259 100644 --- a/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs @@ -21,14 +21,14 @@ public class BicepCompletionHandler : CompletionHandlerBase private readonly ILogger logger; private readonly ICompilationManager compilationManager; private readonly ICompletionProvider completionProvider; - private readonly IFeatureProvider featureProvider; + private readonly IFeatureProviderFactory featureProviderFactory; - public BicepCompletionHandler(ILogger logger, ICompilationManager compilationManager, ICompletionProvider completionProvider, IFeatureProvider featureProvider) + public BicepCompletionHandler(ILogger logger, ICompilationManager compilationManager, ICompletionProvider completionProvider, IFeatureProviderFactory featureProviderFactory) { this.logger = logger; this.compilationManager = compilationManager; this.completionProvider = completionProvider; - this.featureProvider = featureProvider; + this.featureProviderFactory = featureProviderFactory; } public override Task Handle(CompletionParams request, CancellationToken cancellationToken) @@ -42,7 +42,7 @@ public override Task Handle(CompletionParams request, Cancellati } int offset = PositionHelper.GetOffset(compilationContext.LineStarts, request.Position); - var completionContext = BicepCompletionContext.Create(featureProvider, compilationContext.Compilation, offset); + var completionContext = BicepCompletionContext.Create(featureProviderFactory.GetFeatureProvider(request.TextDocument.Uri.ToUri()), compilationContext.Compilation, offset); try { diff --git a/src/Bicep.LangServer/Handlers/BicepDeploymentScopeRequestHandler.cs b/src/Bicep.LangServer/Handlers/BicepDeploymentScopeRequestHandler.cs index 405e8a5a880..2fe1ccfc03f 100644 --- a/src/Bicep.LangServer/Handlers/BicepDeploymentScopeRequestHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepDeploymentScopeRequestHandler.cs @@ -42,38 +42,35 @@ public record BicepDeploymentScopeResponse(string scope, string? template, strin /// public class BicepDeploymentScopeRequestHandler : ExecuteTypedResponseCommandHandlerBase { - private readonly EmitterSettings emitterSettings; private readonly ICompilationManager compilationManager; private readonly IConfigurationManager configurationManager; private readonly IDeploymentFileCompilationCache deploymentFileCompilationCache; - private readonly IFeatureProvider features; + private readonly IFeatureProviderFactory featureProviderFactory; private readonly IFileResolver fileResolver; private readonly IModuleDispatcher moduleDispatcher; private readonly INamespaceProvider namespaceProvider; - private readonly IApiVersionProvider apiVersionProvider; + private readonly IApiVersionProviderFactory apiVersionProviderFactory; public BicepDeploymentScopeRequestHandler( - EmitterSettings emitterSettings, ICompilationManager compilationManager, IConfigurationManager configurationManager, IDeploymentFileCompilationCache deploymentFileCompilationCache, - IFeatureProvider features, + IFeatureProviderFactory featureProviderFactory, IFileResolver fileResolver, IModuleDispatcher moduleDispatcher, INamespaceProvider namespaceProvider, ISerializer serializer, - IApiVersionProvider apiVersionProvider) + IApiVersionProviderFactory apiVersionProviderFactory) : base(LangServerConstants.GetDeploymentScopeCommand, serializer) { this.compilationManager = compilationManager; this.configurationManager = configurationManager; this.deploymentFileCompilationCache = deploymentFileCompilationCache; - this.emitterSettings = emitterSettings; - this.features = features; + this.featureProviderFactory = featureProviderFactory; this.fileResolver = fileResolver; this.moduleDispatcher = moduleDispatcher; this.namespaceProvider = namespaceProvider; - this.apiVersionProvider = apiVersionProvider; + this.apiVersionProviderFactory = apiVersionProviderFactory; } public override Task Handle(BicepDeploymentScopeParams request, CancellationToken cancellationToken) @@ -123,7 +120,8 @@ private string GetCompiledFile(Compilation compilation, DocumentUri documentUri) var stringBuilder = new StringBuilder(); var stringWriter = new StringWriter(stringBuilder); - var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), emitterSettings); + var model = compilation.GetEntrypointSemanticModel(); + var emitter = new TemplateEmitter(model, new EmitterSettings(model.Features)); emitter.Emit(stringWriter); return stringBuilder.ToString(); @@ -137,7 +135,7 @@ private Compilation GetCompilation(DocumentUri documentUri) if (context is null) { SourceFileGrouping sourceFileGrouping = SourceFileGroupingBuilder.Build(this.fileResolver, this.moduleDispatcher, new Workspace(), fileUri); - return new Compilation(features, namespaceProvider, sourceFileGrouping, configurationManager, apiVersionProvider, new LinterAnalyzer()); + return new Compilation(featureProviderFactory, namespaceProvider, sourceFileGrouping, configurationManager, apiVersionProviderFactory, new LinterAnalyzer()); } else { diff --git a/src/Bicep.LangServer/Handlers/BicepGenerateParamsCommandHandler.cs b/src/Bicep.LangServer/Handlers/BicepGenerateParamsCommandHandler.cs index 157bd3fa2ad..638b515b2c3 100644 --- a/src/Bicep.LangServer/Handlers/BicepGenerateParamsCommandHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepGenerateParamsCommandHandler.cs @@ -33,25 +33,23 @@ namespace Bicep.LanguageServer.Handlers public class BicepGenerateParamsCommandHandler : ExecuteTypedResponseCommandHandlerBase { private readonly ICompilationManager compilationManager; - private readonly EmitterSettings emitterSettings; - private readonly IFeatureProvider features; + private readonly IFeatureProviderFactory featureProviderFactory; private readonly IFileResolver fileResolver; private readonly IModuleDispatcher moduleDispatcher; private readonly INamespaceProvider namespaceProvider; private readonly IConfigurationManager configurationManager; - private readonly IApiVersionProvider apiVersionProvider; + private readonly IApiVersionProviderFactory apiVersionProviderFactory; - public BicepGenerateParamsCommandHandler(ICompilationManager compilationManager, ISerializer serializer, IFeatureProvider features, EmitterSettings emitterSettings, INamespaceProvider namespaceProvider, IFileResolver fileResolver, IModuleDispatcher moduleDispatcher, IConfigurationManager configurationManager, IApiVersionProvider apiVersionProvider) + public BicepGenerateParamsCommandHandler(ICompilationManager compilationManager, ISerializer serializer, IFeatureProviderFactory featureProviderFactory, INamespaceProvider namespaceProvider, IFileResolver fileResolver, IModuleDispatcher moduleDispatcher, IConfigurationManager configurationManager, IApiVersionProviderFactory apiVersionProviderFactory) : base(LangServerConstants.GenerateParamsCommand, serializer) { this.compilationManager = compilationManager; - this.emitterSettings = emitterSettings; - this.features = features; + this.featureProviderFactory = featureProviderFactory; this.namespaceProvider = namespaceProvider; this.fileResolver = fileResolver; this.moduleDispatcher = moduleDispatcher; this.configurationManager = configurationManager; - this.apiVersionProvider = apiVersionProvider; + this.apiVersionProviderFactory = apiVersionProviderFactory; } public override Task Handle(string bicepFilePath, CancellationToken cancellationToken) @@ -87,7 +85,7 @@ private string GenerateCompiledParametersFileAndReturnOutputMessage(string bicep if (context is null) { SourceFileGrouping sourceFileGrouping = SourceFileGroupingBuilder.Build(this.fileResolver, this.moduleDispatcher, new Workspace(), fileUri); - compilation = new Compilation(features, namespaceProvider, sourceFileGrouping, configurationManager, apiVersionProvider, new LinterAnalyzer()); + compilation = new Compilation(featureProviderFactory, namespaceProvider, sourceFileGrouping, configurationManager, apiVersionProviderFactory, new LinterAnalyzer()); } else { @@ -104,7 +102,8 @@ private string GenerateCompiledParametersFileAndReturnOutputMessage(string bicep var existingContent = File.Exists(compiledFilePath) ? File.ReadAllText(compiledFilePath) : string.Empty; - var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), emitterSettings); + var model = compilation.GetEntrypointSemanticModel(); + var emitter = new TemplateEmitter(model, new EmitterSettings(model.Features)); using var fileStream = new FileStream(compiledFilePath, FileMode.Create, FileAccess.Write); var result = emitter.EmitParametersFile(fileStream, existingContent); diff --git a/src/Bicep.LangServer/ParamsHandlers/BicepParamsCompletionHandler.cs b/src/Bicep.LangServer/ParamsHandlers/BicepParamsCompletionHandler.cs index 61e99b90b35..2f3f85a5082 100644 --- a/src/Bicep.LangServer/ParamsHandlers/BicepParamsCompletionHandler.cs +++ b/src/Bicep.LangServer/ParamsHandlers/BicepParamsCompletionHandler.cs @@ -20,14 +20,14 @@ public class BicepParamsCompletionHandler : CompletionHandlerBase { private readonly ILogger logger; private readonly ICompletionProvider completionProvider; - private readonly IFeatureProvider featureProvider; + private readonly IFeatureProviderFactory featureProviderFactory; private readonly IParamsCompilationManager paramsCompilationManager; - public BicepParamsCompletionHandler(ILogger logger, ICompletionProvider completionProvider, IFeatureProvider featureProvider, IParamsCompilationManager paramsCompilationManager) + public BicepParamsCompletionHandler(ILogger logger, ICompletionProvider completionProvider, IFeatureProviderFactory featureProviderFactory, IParamsCompilationManager paramsCompilationManager) { this.logger = logger; this.completionProvider = completionProvider; - this.featureProvider = featureProvider; + this.featureProviderFactory = featureProviderFactory; this.paramsCompilationManager = paramsCompilationManager; } @@ -35,7 +35,7 @@ public override Task Handle(CompletionParams request, Cancellati { var completions = Enumerable.Empty(); - if (featureProvider.ParamsFilesEnabled) + if (featureProviderFactory.GetFeatureProvider(request.TextDocument.Uri.ToUri()).ParamsFilesEnabled) { var paramsCompilationContext = this.paramsCompilationManager.GetCompilation(request.TextDocument.Uri); if (paramsCompilationContext is null) diff --git a/src/Bicep.LangServer/ParamsHandlers/BicepParamsDefinitionHandler.cs b/src/Bicep.LangServer/ParamsHandlers/BicepParamsDefinitionHandler.cs index e291c53ba83..434f4863b96 100644 --- a/src/Bicep.LangServer/ParamsHandlers/BicepParamsDefinitionHandler.cs +++ b/src/Bicep.LangServer/ParamsHandlers/BicepParamsDefinitionHandler.cs @@ -20,20 +20,20 @@ public class BicepParamsDefinitionHandler : DefinitionHandlerBase { private readonly ISymbolResolver symbolResolver; private readonly IParamsCompilationManager paramsCompilationManager; - private readonly IFeatureProvider featureProvider; + private readonly IFeatureProviderFactory featureProviderFactory; public BicepParamsDefinitionHandler( ISymbolResolver symbolResolver, IParamsCompilationManager paramsCompilationManager, - IFeatureProvider featureProvider) : base() + IFeatureProviderFactory featureProviderFactory) : base() { this.symbolResolver = symbolResolver; this.paramsCompilationManager = paramsCompilationManager; - this.featureProvider = featureProvider; + this.featureProviderFactory = featureProviderFactory; } public override Task Handle(DefinitionParams request, CancellationToken cancellationToken) { - if (featureProvider.ParamsFilesEnabled) + if (featureProviderFactory.GetFeatureProvider(request.TextDocument.Uri.ToUri()).ParamsFilesEnabled) { var paramsContext = this.paramsCompilationManager.GetCompilation(request.TextDocument.Uri); if (paramsContext is null) diff --git a/src/Bicep.LangServer/ParamsHandlers/BicepParamsTextDocumentSyncHandler.cs b/src/Bicep.LangServer/ParamsHandlers/BicepParamsTextDocumentSyncHandler.cs index 26de957af50..88720d422a4 100644 --- a/src/Bicep.LangServer/ParamsHandlers/BicepParamsTextDocumentSyncHandler.cs +++ b/src/Bicep.LangServer/ParamsHandlers/BicepParamsTextDocumentSyncHandler.cs @@ -20,12 +20,12 @@ namespace Bicep.LanguageServer.ParamsHandlers internal class BicepParamsTextDocumentSyncHandler : TextDocumentSyncHandlerBase { private readonly IParamsCompilationManager paramsCompilationManager; - private readonly IFeatureProvider featureProvider; + private readonly IFeatureProviderFactory featureProviderFactory; - public BicepParamsTextDocumentSyncHandler(IParamsCompilationManager paramsCompilationManager, IFeatureProvider featureProvider) + public BicepParamsTextDocumentSyncHandler(IParamsCompilationManager paramsCompilationManager, IFeatureProviderFactory featureProviderFactory) { this.paramsCompilationManager = paramsCompilationManager; - this.featureProvider = featureProvider; + this.featureProviderFactory = featureProviderFactory; } public override TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri) @@ -40,7 +40,7 @@ public override Task Handle(DidChangeTextDocumentParams request, Cancellat var documentUri = request.TextDocument.Uri; - if (featureProvider.ParamsFilesEnabled) + if (featureProviderFactory.GetFeatureProvider(documentUri.ToUri()).ParamsFilesEnabled) { this.paramsCompilationManager.UpsertCompilation(documentUri, request.TextDocument.Version, contents); } @@ -50,9 +50,10 @@ public override Task Handle(DidChangeTextDocumentParams request, Cancellat public override Task Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken) { - if (featureProvider.ParamsFilesEnabled) + var documentUri = request.TextDocument.Uri; + if (featureProviderFactory.GetFeatureProvider(documentUri.ToUri()).ParamsFilesEnabled) { - this.paramsCompilationManager.UpsertCompilation(request.TextDocument.Uri, request.TextDocument.Version, request.TextDocument.Text, request.TextDocument.LanguageId); + this.paramsCompilationManager.UpsertCompilation(documentUri, request.TextDocument.Version, request.TextDocument.Text, request.TextDocument.LanguageId); } return Unit.Task; @@ -62,9 +63,10 @@ public override Task Handle(DidOpenTextDocumentParams request, Cancellatio public override Task Handle(DidCloseTextDocumentParams request, CancellationToken cancellationToken) { - if (featureProvider.ParamsFilesEnabled) + var documentUri = request.TextDocument.Uri; + if (featureProviderFactory.GetFeatureProvider(documentUri.ToUri()).ParamsFilesEnabled) { - this.paramsCompilationManager.CloseCompilation(request.TextDocument.Uri); + this.paramsCompilationManager.CloseCompilation(documentUri); } return Unit.Task; diff --git a/src/Bicep.LangServer/Providers/BicepCompilationProvider.cs b/src/Bicep.LangServer/Providers/BicepCompilationProvider.cs index 0a9f361d3f1..e0b79d5ea95 100644 --- a/src/Bicep.LangServer/Providers/BicepCompilationProvider.cs +++ b/src/Bicep.LangServer/Providers/BicepCompilationProvider.cs @@ -22,16 +22,16 @@ namespace Bicep.LanguageServer.Providers public class BicepCompilationProvider : ICompilationProvider { private readonly IConfigurationManager configurationManager; - private readonly IFeatureProvider features; - private readonly IApiVersionProvider apiVersionProvider; + private readonly IFeatureProviderFactory featureProviderFactory; + private readonly IApiVersionProviderFactory apiVersionProviderFactory; private readonly INamespaceProvider namespaceProvider; private readonly IFileResolver fileResolver; private readonly IModuleDispatcher moduleDispatcher; - public BicepCompilationProvider(IFeatureProvider features, INamespaceProvider namespaceProvider, IFileResolver fileResolver, IModuleDispatcher moduleDispatcher, IApiVersionProvider apiVersionProvider, IConfigurationManager configurationManager) + public BicepCompilationProvider(IFeatureProviderFactory featureProviderFactory, INamespaceProvider namespaceProvider, IFileResolver fileResolver, IModuleDispatcher moduleDispatcher, IApiVersionProviderFactory apiVersionProviderFactory, IConfigurationManager configurationManager) { - this.features = features; - this.apiVersionProvider = apiVersionProvider; + this.featureProviderFactory = featureProviderFactory; + this.apiVersionProviderFactory = apiVersionProviderFactory; this.namespaceProvider = namespaceProvider; this.fileResolver = fileResolver; this.moduleDispatcher = moduleDispatcher; @@ -52,7 +52,7 @@ public CompilationContext Update(IReadOnlyWorkspace workspace, CompilationContex private CompilationContext CreateContext(SourceFileGrouping syntaxTreeGrouping, ImmutableDictionary modelLookup, LinterAnalyzer linterAnalyzer) { - var compilation = new Compilation(features, namespaceProvider, syntaxTreeGrouping, configurationManager, apiVersionProvider, linterAnalyzer, modelLookup); + var compilation = new Compilation(featureProviderFactory, namespaceProvider, syntaxTreeGrouping, configurationManager, apiVersionProviderFactory, linterAnalyzer, modelLookup); return new CompilationContext(compilation); } } diff --git a/src/Bicep.LangServer/Server.cs b/src/Bicep.LangServer/Server.cs index a2f3b177389..e484460c0bb 100644 --- a/src/Bicep.LangServer/Server.cs +++ b/src/Bicep.LangServer/Server.cs @@ -44,7 +44,7 @@ public record CreationOptions( ISnippetsProvider? SnippetsProvider = null, INamespaceProvider? NamespaceProvider = null, IFileResolver? FileResolver = null, - IFeatureProvider? Features = null, + IFeatureProviderFactory? FeatureProviderFactory = null, IModuleRestoreScheduler? ModuleRestoreScheduler = null, Action? onRegisterServices = null); @@ -105,12 +105,12 @@ public async Task RunAsync(CancellationToken cancellationToken) server.LogInfo($"Running on processId {Environment.ProcessId}"); - if (FeatureProvider.TracingEnabled) + if (IFeatureProvider.TracingEnabled) { Trace.Listeners.Add(new ServerLogTraceListener(server)); } - using (FeatureProvider.TracingEnabled ? AzureEventSourceListenerFactory.Create(FeatureProvider.TracingVerbosity) : null) + using (IFeatureProvider.TracingEnabled ? AzureEventSourceListenerFactory.Create(IFeatureProvider.TracingVerbosity) : null) { var scheduler = server.GetRequiredService(); scheduler.Start(); @@ -127,8 +127,7 @@ private static void RegisterServices(CreationOptions creationOptions, IServiceCo services.AddSingletonOrInstance(creationOptions.NamespaceProvider); services.AddSingletonOrInstance(creationOptions.SnippetsProvider); services.AddSingletonOrInstance(creationOptions.FileResolver); - services.AddSingletonOrInstance(creationOptions.Features); - services.AddSingleton(); + services.AddSingletonOrInstance(creationOptions.FeatureProviderFactory); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -150,7 +149,7 @@ private static void RegisterServices(CreationOptions creationOptions, IServiceCo services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); } diff --git a/src/Bicep.LangServer/Snippets/SnippetsProvider.cs b/src/Bicep.LangServer/Snippets/SnippetsProvider.cs index 2cbd992c687..906e203ea08 100644 --- a/src/Bicep.LangServer/Snippets/SnippetsProvider.cs +++ b/src/Bicep.LangServer/Snippets/SnippetsProvider.cs @@ -65,18 +65,18 @@ public class SnippetsProvider : ISnippetsProvider "properties", "dependsOn"); - private readonly IFeatureProvider features; - private readonly IApiVersionProvider apiVersionProvider; + private readonly IFeatureProviderFactory featureProviderFactory; + private readonly IApiVersionProviderFactory apiVersionProviderFactory; private readonly INamespaceProvider namespaceProvider; private readonly IFileResolver fileResolver; private readonly LinterAnalyzer linterAnalyzer; private readonly IConfigurationManager configurationManager; private readonly IModuleDispatcher moduleDispatcher; - public SnippetsProvider(IFeatureProvider features, INamespaceProvider namespaceProvider, IFileResolver fileResolver, IConfigurationManager configurationManager, IApiVersionProvider apiVersionProvider, IModuleDispatcher moduleDispatcher) + public SnippetsProvider(IFeatureProviderFactory featureProviderFactory, INamespaceProvider namespaceProvider, IFileResolver fileResolver, IConfigurationManager configurationManager, IApiVersionProviderFactory apiVersionProviderFactory, IModuleDispatcher moduleDispatcher) { - this.features = features; - this.apiVersionProvider = apiVersionProvider; + this.featureProviderFactory = featureProviderFactory; + this.apiVersionProviderFactory = apiVersionProviderFactory; this.moduleDispatcher = moduleDispatcher; this.namespaceProvider = namespaceProvider; this.fileResolver = fileResolver; @@ -225,7 +225,7 @@ private ImmutableDictionary var sourceFileGrouping = SourceFileGroupingBuilder.Build(fileResolver, moduleDispatcher, workspace, bicepFile.FileUri, false); - Compilation compilation = new Compilation(features, namespaceProvider, sourceFileGrouping, configurationManager, apiVersionProvider, linterAnalyzer); + Compilation compilation = new Compilation(featureProviderFactory, namespaceProvider, sourceFileGrouping, configurationManager, apiVersionProviderFactory, linterAnalyzer); SemanticModel semanticModel = compilation.GetEntrypointSemanticModel(); diff --git a/src/Bicep.Wasm/Interop.cs b/src/Bicep.Wasm/Interop.cs index 0856dbea6d3..85d6b88fe25 100644 --- a/src/Bicep.Wasm/Interop.cs +++ b/src/Bicep.Wasm/Interop.cs @@ -26,10 +26,12 @@ namespace Bicep.Wasm { public class Interop { - private static readonly IFeatureProvider features = new FeatureProvider(); + private static readonly IFeatureProviderFactory featureProviderFactory = IFeatureProviderFactory.WithStaticFeatureProvider(IFeatureProviderFactory.FeatureDefaults); private static readonly INamespaceProvider namespaceProvider = new DefaultNamespaceProvider(new AzResourceTypeLoader()); + private static readonly IApiVersionProviderFactory apiVersionProviderFactory = new ApiVersionProviderFactory(featureProviderFactory, namespaceProvider); + private readonly IJSRuntime jsRuntime; public Interop(IJSRuntime jsRuntime) @@ -64,7 +66,7 @@ public DecompileResult Decompile(string jsonContent) try { var bicepUri = PathHelper.ChangeToBicepExtension(jsonUri); - var decompiler = new TemplateDecompiler(features, namespaceProvider, fileResolver, new EmptyModuleRegistryProvider()); + var decompiler = new TemplateDecompiler(featureProviderFactory, namespaceProvider, fileResolver, new EmptyModuleRegistryProvider(), apiVersionProviderFactory); var (entrypointUri, filesToSave) = decompiler.DecompileFileWithModules(jsonUri, bicepUri); return new DecompileResult(filesToSave[entrypointUri], null); @@ -135,7 +137,7 @@ private static (string, IEnumerable) CompileInternal(string content) { var compilation = GetCompilation(content); var lineStarts = compilation.SourceFileGrouping.EntryPoint.LineStarts; - var emitterSettings = new EmitterSettings(features); + var emitterSettings = new EmitterSettings(featureProviderFactory.GetFeatureProvider(new Uri("inmemory:///main.bicep"))); var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), emitterSettings); // memory stream is not ideal for frequent large allocations @@ -170,7 +172,7 @@ private static Compilation GetCompilation(string fileContents) var dispatcher = new ModuleDispatcher(new EmptyModuleRegistryProvider(), configurationManager); var sourceFileGrouping = SourceFileGroupingBuilder.Build(fileResolver, dispatcher, workspace, fileUri); - return new Compilation(features, namespaceProvider, sourceFileGrouping, configurationManager, new ApiVersionProvider(features, namespaceProvider), new LinterAnalyzer()); + return new Compilation(featureProviderFactory, namespaceProvider, sourceFileGrouping, configurationManager, apiVersionProviderFactory, new LinterAnalyzer()); } private static string ReadStreamToEnd(Stream stream) From a7cd1251d912fcf8fdc02a5a4988a60559fcc92c Mon Sep 17 00:00:00 2001 From: Jonathan Eskew Date: Tue, 4 Oct 2022 02:30:30 -0400 Subject: [PATCH 2/6] Leave only bicepconfig.json as a feature flag source --- src/Bicep.Cli/Program.cs | 4 +- .../ApiVersion/ApiVersionProviderTests.cs | 3 +- .../Features/FeatureProviderTests.cs | 45 ---------- .../BicepConfigFeatureProviderSource.cs | 23 ----- .../Features/DefaultsFeatureProvider.cs | 25 ------ ...vironmentVariablesFeatureProviderSource.cs | 34 -------- src/Bicep.Core/Features/FeatureProvider.cs | 87 ++++++++----------- .../Features/FeatureProviderFactory.cs | 7 +- src/Bicep.Core/Features/IFeatureProvider.cs | 15 ---- .../Features/IFeatureProviderFactory.cs | 2 - .../Features/IFeatureProviderSource.cs | 31 ------- src/Bicep.LangServer/Server.cs | 4 +- src/Bicep.Wasm/Interop.cs | 5 +- 13 files changed, 47 insertions(+), 238 deletions(-) delete mode 100644 src/Bicep.Core/Features/BicepConfigFeatureProviderSource.cs delete mode 100644 src/Bicep.Core/Features/DefaultsFeatureProvider.cs delete mode 100644 src/Bicep.Core/Features/EnvironmentVariablesFeatureProviderSource.cs delete mode 100644 src/Bicep.Core/Features/IFeatureProviderSource.cs diff --git a/src/Bicep.Cli/Program.cs b/src/Bicep.Cli/Program.cs index dd229d2e34e..9e310b3aa33 100644 --- a/src/Bicep.Cli/Program.cs +++ b/src/Bicep.Cli/Program.cs @@ -46,13 +46,13 @@ public static async Task Main(string[] args) BicepDeploymentsInterop.Initialize(); - if (IFeatureProvider.TracingEnabled) + if (FeatureProvider.TracingEnabled) { Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); } // this event listener picks up SDK events and writes them to Trace.WriteLine() - using (IFeatureProvider.TracingEnabled ? AzureEventSourceListenerFactory.Create(IFeatureProvider.TracingVerbosity) : null) + using (FeatureProvider.TracingEnabled ? AzureEventSourceListenerFactory.Create(FeatureProvider.TracingVerbosity) : null) { var program = new Program(new InvocationContext( new AzResourceTypeLoader(), diff --git a/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs b/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs index 594a2ed0a36..49b30685edf 100644 --- a/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs +++ b/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using Bicep.Core.Analyzers.Linter.ApiVersions; +using Bicep.Core.Configuration; using Bicep.Core.Features; using Bicep.Core.Semantics.Namespaces; using Bicep.Core.TypeSystem; @@ -82,6 +83,6 @@ public void GetResourceTypeNames_SeparateScopes() } private ApiVersionProvider CreateDefaultApiVersionProvider() - => new ApiVersionProvider(IFeatureProviderFactory.FeatureDefaults, new DefaultNamespaceProvider(new AzResourceTypeLoader())); + => new ApiVersionProvider(new FeatureProvider(IConfigurationManager.GetBuiltInConfiguration()), new DefaultNamespaceProvider(new AzResourceTypeLoader())); } } diff --git a/src/Bicep.Core.UnitTests/Features/FeatureProviderTests.cs b/src/Bicep.Core.UnitTests/Features/FeatureProviderTests.cs index e1e3f5ee79c..2bbc257324b 100644 --- a/src/Bicep.Core.UnitTests/Features/FeatureProviderTests.cs +++ b/src/Bicep.Core.UnitTests/Features/FeatureProviderTests.cs @@ -6,13 +6,11 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Abstractions.TestingHelpers; -using System.Linq; using Bicep.Core.Configuration; using Bicep.Core.Features; using Bicep.Core.UnitTests.Assertions; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; namespace Bicep.Core.UnitTests.Features; @@ -22,49 +20,6 @@ public class FeatureProviderTests [NotNull] public TestContext? TestContext { get; set; } - [TestMethod] - public void PropertyLookup_FallsBackToDefaultIfNoSourcesSupplyIt() - { - var sources = Enumerable.Range(0, 3).Select(i => { - var mock = new Mock(MockBehavior.Strict); - mock.Setup(s => s.Priority).Returns((sbyte) i); - mock.Setup(s => s.ImportsEnabled).Returns(new Nullable()); - return mock; - }).ToArray(); - var defaults = new Mock(MockBehavior.Strict); - defaults.Setup(d => d.ImportsEnabled).Returns(true); - - var sut = new FeatureProvider(defaults.Object, sources.Select(s => s.Object)); - sut.ImportsEnabled.Should().BeTrue(); - - foreach (var source in sources) - { - source.Verify(s => s.ImportsEnabled, Times.Once); - } - } - - [TestMethod] - public void PropertyLookup_PollsSourcesInPriorityOrder() - { - var sources = Enumerable.Range(1, 3).Select(i => { - var mock = new Mock(MockBehavior.Strict); - mock.Setup(s => s.Priority).Returns((sbyte) -i); - mock.Setup(s => s.ImportsEnabled).Returns(i == 3 ? new Nullable(true) : new Nullable(false)); - return mock; - }).ToArray(); - var defaults = new Mock(MockBehavior.Strict); - - var sut = new FeatureProvider(defaults.Object, sources.Select(s => s.Object)); - sut.ImportsEnabled.Should().BeTrue(); - - defaults.Verify(d => d.ImportsEnabled, Times.Never); - sources[^1].Verify(s => s.ImportsEnabled, Times.Once); - foreach(var source in sources[..^1]) - { - source.Verify(s => s.ImportsEnabled, Times.Never); - } - } - [TestMethod] public void PropertyLookup_WithNothingConfigured_ReturnsDefault() { diff --git a/src/Bicep.Core/Features/BicepConfigFeatureProviderSource.cs b/src/Bicep.Core/Features/BicepConfigFeatureProviderSource.cs deleted file mode 100644 index 6879aa050eb..00000000000 --- a/src/Bicep.Core/Features/BicepConfigFeatureProviderSource.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -using Bicep.Core.Configuration; - -namespace Bicep.Core.Features; - -internal class BicepConfigFeatureProviderSource : IFeatureProviderSource -{ - private readonly RootConfiguration configuration; - - public BicepConfigFeatureProviderSource(RootConfiguration configuration) - { - this.configuration = configuration; - } - - sbyte IFeatureProviderSource.Priority => 1; - public string? CacheRootDirectory => configuration.CacheRootDirectory; - public bool? SymbolicNameCodegenEnabled => configuration.ExperimentalFeaturesEnabled.IsEnabled("symbolicNameCodegen"); - public bool? ImportsEnabled => configuration.ExperimentalFeaturesEnabled.IsEnabled("imports"); - public bool? ResourceTypedParamsAndOutputsEnabled => configuration.ExperimentalFeaturesEnabled.IsEnabled("resourceTypedParamsAndOutputs"); - public bool? SourceMappingEnabled => configuration.ExperimentalFeaturesEnabled.IsEnabled("sourceMapping"); - public bool? ParamsFilesEnabled => configuration.ExperimentalFeaturesEnabled.IsEnabled("paramsFiles"); -} diff --git a/src/Bicep.Core/Features/DefaultsFeatureProvider.cs b/src/Bicep.Core/Features/DefaultsFeatureProvider.cs deleted file mode 100644 index 8f55652c434..00000000000 --- a/src/Bicep.Core/Features/DefaultsFeatureProvider.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -using System; -using System.IO; - -namespace Bicep.Core.Features; - -internal class DefaultsFeatureProvider : IFeatureProvider -{ - public string CacheRootDirectory => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".bicep"); - - public bool RegistryEnabled => true; - - public bool SymbolicNameCodegenEnabled => false; - - public bool ImportsEnabled => false; - - public bool ResourceTypedParamsAndOutputsEnabled => false; - - public string AssemblyVersion => ThisAssembly.AssemblyFileVersion; - - public bool SourceMappingEnabled => false; - - public bool ParamsFilesEnabled => false; -} diff --git a/src/Bicep.Core/Features/EnvironmentVariablesFeatureProviderSource.cs b/src/Bicep.Core/Features/EnvironmentVariablesFeatureProviderSource.cs deleted file mode 100644 index 0dcd402925d..00000000000 --- a/src/Bicep.Core/Features/EnvironmentVariablesFeatureProviderSource.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -using System; -using System.Diagnostics; - -namespace Bicep.Core.Features; - -internal class EnvironmentVariablesFeatureProviderSource : IFeatureProviderSource -{ - sbyte IFeatureProviderSource.Priority => 0; - - public string? CacheRootDirectory => Environment.GetEnvironmentVariable("BICEP_CACHE_DIRECTORY"); - - public bool? SymbolicNameCodegenEnabled => ReadBooleanEnvVar("BICEP_SYMBOLIC_NAME_CODEGEN_EXPERIMENTAL"); - - public bool? ImportsEnabled => ReadBooleanEnvVar("BICEP_IMPORTS_ENABLED_EXPERIMENTAL"); - - public bool? ResourceTypedParamsAndOutputsEnabled => ReadBooleanEnvVar("BICEP_RESOURCE_TYPED_PARAMS_AND_OUTPUTS_EXPERIMENTAL"); - - public bool? SourceMappingEnabled => ReadBooleanEnvVar("BICEP_SOURCEMAPPING_ENABLED"); - - public bool? paramsFilesEnabledLazy => ReadBooleanEnvVar("BICEP_PARAMS_FILES_ENABLED"); - - private static bool? ReadBooleanEnvVar(string envVar) - { - if (bool.TryParse(Environment.GetEnvironmentVariable(envVar), out bool value)) - { - Trace.WriteLine("Enabling experimental features via environment variables is deprecated. Please migrate to enabling/disabling features via bicepconfig.json"); - return value; - } - - return null; - } -} diff --git a/src/Bicep.Core/Features/FeatureProvider.cs b/src/Bicep.Core/Features/FeatureProvider.cs index 71584166457..3e871f75c76 100644 --- a/src/Bicep.Core/Features/FeatureProvider.cs +++ b/src/Bicep.Core/Features/FeatureProvider.cs @@ -2,80 +2,67 @@ // Licensed under the MIT License. using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; +using System.IO; +using Bicep.Core.Configuration; namespace Bicep.Core.Features { public class FeatureProvider : IFeatureProvider { - private readonly IEnumerable providerChain; - private readonly IFeatureProvider defaultsProvider; - private readonly Lazy cacheRootDirectoryLazy; - private readonly Lazy assemblyVersionLazy; - private readonly Lazy registryEnabledLazy; - private readonly Lazy symbolicNameCodegenEnabledLazy; - private readonly Lazy importsEnabledLazy; - private readonly Lazy resourceTypedParamsAndOutputsEnabledLazy; - private readonly Lazy sourceMappingEnabledLazy; - private readonly Lazy paramsFilesEnabledLazy; - - public FeatureProvider(IFeatureProvider defaultsProvider, IEnumerable sources) + private readonly RootConfiguration configuration; + + public FeatureProvider(RootConfiguration configuration) { - this.defaultsProvider = defaultsProvider; - providerChain = sources.OrderBy(s => s.Priority).ToList(); - - cacheRootDirectoryLazy = new(() => LookupFeature(s => s.CacheRootDirectory, d => d.CacheRootDirectory), LazyThreadSafetyMode.PublicationOnly); - assemblyVersionLazy = new(() => LookupFeature(s => s.AssemblyVersion, d => d.AssemblyVersion), LazyThreadSafetyMode.PublicationOnly); - registryEnabledLazy = new(() => LookupFeature(s => s.RegistryEnabled, d => d.RegistryEnabled), LazyThreadSafetyMode.PublicationOnly); - symbolicNameCodegenEnabledLazy = new(() => LookupFeature(s => s.SymbolicNameCodegenEnabled, d => d.SymbolicNameCodegenEnabled), LazyThreadSafetyMode.PublicationOnly); - importsEnabledLazy = new(() => LookupFeature(s => s.ImportsEnabled, d => d.ImportsEnabled), LazyThreadSafetyMode.PublicationOnly); - resourceTypedParamsAndOutputsEnabledLazy = new(() => LookupFeature(s => s.ResourceTypedParamsAndOutputsEnabled, d => d.ResourceTypedParamsAndOutputsEnabled), LazyThreadSafetyMode.PublicationOnly); - sourceMappingEnabledLazy = new(() => LookupFeature(s => s.SourceMappingEnabled, d => d.SourceMappingEnabled), LazyThreadSafetyMode.PublicationOnly); - paramsFilesEnabledLazy = new(() => LookupFeature(s => s.ParamsFilesEnabled, d => d.ParamsFilesEnabled), LazyThreadSafetyMode.PublicationOnly); + this.configuration = configuration; } - public string CacheRootDirectory => cacheRootDirectoryLazy.Value; + public string CacheRootDirectory => GetCacheRootDirectory(configuration.CacheRootDirectory); + + public bool RegistryEnabled => true; + + public bool SymbolicNameCodegenEnabled => LookupFeatureFlag("symbolicNameCodegen", false); + + public bool ImportsEnabled => LookupFeatureFlag("imports", false); + + public bool ResourceTypedParamsAndOutputsEnabled => LookupFeatureFlag("resourceTypedParamsAndOutputs", false); - public bool RegistryEnabled => registryEnabledLazy.Value; + public string AssemblyVersion => ThisAssembly.AssemblyFileVersion; - public bool SymbolicNameCodegenEnabled => symbolicNameCodegenEnabledLazy.Value; + public bool SourceMappingEnabled => LookupFeatureFlag("sourceMapping", false); - public bool ImportsEnabled => importsEnabledLazy.Value; + public bool ParamsFilesEnabled => LookupFeatureFlag("paramsFiles", false); - public bool ResourceTypedParamsAndOutputsEnabled => resourceTypedParamsAndOutputsEnabledLazy.Value; + private bool LookupFeatureFlag(string featureName, bool defaultValue) + => configuration.ExperimentalFeaturesEnabled.IsEnabled(featureName) is bool enabled ? enabled : defaultValue; - public string AssemblyVersion => assemblyVersionLazy.Value; + public static bool TracingEnabled => ReadBooleanEnvVar("BICEP_TRACING_ENABLED", defaultValue: false); - public bool SourceMappingEnabled => sourceMappingEnabledLazy.Value; + public static TraceVerbosity TracingVerbosity => ReadEnumEnvvar("BICEP_TRACING_VERBOSITY", TraceVerbosity.Basic); - public bool ParamsFilesEnabled => paramsFilesEnabledLazy.Value; + private static bool ReadBooleanEnvVar(string envVar, bool defaultValue) + => bool.TryParse(Environment.GetEnvironmentVariable(envVar), out var value) ? value : defaultValue; - private T LookupFeature(Func sourceLookup, Func defaultLookup) + private static T ReadEnumEnvvar(string envVar, T defaultValue) where T : struct { - foreach (var link in providerChain) - { - if (sourceLookup.Invoke(link) is T resolved) - { - return resolved; - } - } + var str = Environment.GetEnvironmentVariable(envVar); + return Enum.TryParse(str, true, out var value) ? value : defaultValue; + } + + private static string GetDefaultCachePath() + { + string basePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - return defaultLookup.Invoke(defaultsProvider); + return Path.Combine(basePath, ".bicep"); } - private T LookupFeature(Func> sourceLookup, Func defaultLookup) where T : struct + private static string GetCacheRootDirectory(string? customPath) { - foreach (var link in providerChain) + if (string.IsNullOrWhiteSpace(customPath)) { - if (sourceLookup.Invoke(link) is T resolved) - { - return resolved; - } + return GetDefaultCachePath(); } - return defaultLookup.Invoke(defaultsProvider); + return customPath; } } } diff --git a/src/Bicep.Core/Features/FeatureProviderFactory.cs b/src/Bicep.Core/Features/FeatureProviderFactory.cs index 5a42c69cd74..01e8eb2582f 100644 --- a/src/Bicep.Core/Features/FeatureProviderFactory.cs +++ b/src/Bicep.Core/Features/FeatureProviderFactory.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using Bicep.Core.Configuration; namespace Bicep.Core.Features; @@ -11,13 +8,11 @@ namespace Bicep.Core.Features; public class FeatureProviderFactory : IFeatureProviderFactory { private readonly IConfigurationManager configurationManager; - private readonly IEnumerable sources = ImmutableArray.Create(new EnvironmentVariablesFeatureProviderSource()); public FeatureProviderFactory(IConfigurationManager configurationManager) { this.configurationManager = configurationManager; } - public IFeatureProvider GetFeatureProvider(Uri templateUri) => new FeatureProvider(IFeatureProviderFactory.FeatureDefaults, - sources.Append(new BicepConfigFeatureProviderSource(configurationManager.GetConfiguration(templateUri)))); + public IFeatureProvider GetFeatureProvider(Uri templateUri) => new FeatureProvider(configurationManager.GetConfiguration(templateUri)); } diff --git a/src/Bicep.Core/Features/IFeatureProvider.cs b/src/Bicep.Core/Features/IFeatureProvider.cs index 6f0c6ccdf04..2a143a20b23 100644 --- a/src/Bicep.Core/Features/IFeatureProvider.cs +++ b/src/Bicep.Core/Features/IFeatureProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; - namespace Bicep.Core.Features { public interface IFeatureProvider @@ -22,18 +20,5 @@ public interface IFeatureProvider bool SourceMappingEnabled { get; } bool ParamsFilesEnabled { get; } - - static bool TracingEnabled => ReadBooleanEnvVar("BICEP_TRACING_ENABLED", defaultValue: false); - - static TraceVerbosity TracingVerbosity => ReadEnumEnvvar("BICEP_TRACING_VERBOSITY", TraceVerbosity.Basic); - - private static bool ReadBooleanEnvVar(string envVar, bool defaultValue) - => bool.TryParse(Environment.GetEnvironmentVariable(envVar), out var value) ? value : defaultValue; - - private static T ReadEnumEnvvar(string envVar, T defaultValue) where T : struct - { - var str = Environment.GetEnvironmentVariable(envVar); - return Enum.TryParse(str, true, out var value) ? value : defaultValue; - } } } diff --git a/src/Bicep.Core/Features/IFeatureProviderFactory.cs b/src/Bicep.Core/Features/IFeatureProviderFactory.cs index a0363156340..3fe3b4d8ea1 100644 --- a/src/Bicep.Core/Features/IFeatureProviderFactory.cs +++ b/src/Bicep.Core/Features/IFeatureProviderFactory.cs @@ -8,8 +8,6 @@ public interface IFeatureProviderFactory { IFeatureProvider GetFeatureProvider(Uri templateUri); - static IFeatureProvider FeatureDefaults = new DefaultsFeatureProvider(); - static IFeatureProviderFactory WithStaticFeatureProvider(IFeatureProvider featureProvider) => new ConstantFeatureProviderFactory(featureProvider); diff --git a/src/Bicep.Core/Features/IFeatureProviderSource.cs b/src/Bicep.Core/Features/IFeatureProviderSource.cs deleted file mode 100644 index e44b7088847..00000000000 --- a/src/Bicep.Core/Features/IFeatureProviderSource.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Bicep.Core.Features; - -public interface IFeatureProviderSource -{ - /// - /// A priority ranking for this source. Sources that report numerically lower priority rankings will be queried - /// first (e.g., a source that returns 10 for this property will override a source that reports 11 for this) - /// property. The values 0 and 1 are used by the environment variable and bicepconfig.json sources, so use a - /// negative value to take precedence over the sources built into Bicep.Core. - /// - sbyte Priority { get; } - - string? AssemblyVersion => default; - - string? CacheRootDirectory => default; - - bool? RegistryEnabled => default; - - bool? SymbolicNameCodegenEnabled => default; - - bool? ImportsEnabled => default; - - bool? ResourceTypedParamsAndOutputsEnabled => default; - - bool? SourceMappingEnabled => default; - - bool? ParamsFilesEnabled => default; -} diff --git a/src/Bicep.LangServer/Server.cs b/src/Bicep.LangServer/Server.cs index e484460c0bb..a117449c1d9 100644 --- a/src/Bicep.LangServer/Server.cs +++ b/src/Bicep.LangServer/Server.cs @@ -105,12 +105,12 @@ public async Task RunAsync(CancellationToken cancellationToken) server.LogInfo($"Running on processId {Environment.ProcessId}"); - if (IFeatureProvider.TracingEnabled) + if (FeatureProvider.TracingEnabled) { Trace.Listeners.Add(new ServerLogTraceListener(server)); } - using (IFeatureProvider.TracingEnabled ? AzureEventSourceListenerFactory.Create(IFeatureProvider.TracingVerbosity) : null) + using (FeatureProvider.TracingEnabled ? AzureEventSourceListenerFactory.Create(FeatureProvider.TracingVerbosity) : null) { var scheduler = server.GetRequiredService(); scheduler.Start(); diff --git a/src/Bicep.Wasm/Interop.cs b/src/Bicep.Wasm/Interop.cs index 85d6b88fe25..a1df7933024 100644 --- a/src/Bicep.Wasm/Interop.cs +++ b/src/Bicep.Wasm/Interop.cs @@ -26,7 +26,9 @@ namespace Bicep.Wasm { public class Interop { - private static readonly IFeatureProviderFactory featureProviderFactory = IFeatureProviderFactory.WithStaticFeatureProvider(IFeatureProviderFactory.FeatureDefaults); + private static readonly IConfigurationManager configurationManager = IConfigurationManager.WithStaticConfiguration(IConfigurationManager.GetBuiltInConfiguration().WithAllAnalyzersDisabled()); + + private static readonly IFeatureProviderFactory featureProviderFactory = new FeatureProviderFactory(configurationManager); private static readonly INamespaceProvider namespaceProvider = new DefaultNamespaceProvider(new AzResourceTypeLoader()); @@ -168,7 +170,6 @@ private static Compilation GetCompilation(string fileContents) workspace.UpsertSourceFile(sourceFile); var fileResolver = new FileResolver(); - var configurationManager = IConfigurationManager.WithStaticConfiguration(IConfigurationManager.GetBuiltInConfiguration().WithAllAnalyzersDisabled()); var dispatcher = new ModuleDispatcher(new EmptyModuleRegistryProvider(), configurationManager); var sourceFileGrouping = SourceFileGroupingBuilder.Build(fileResolver, dispatcher, workspace, fileUri); From 7308527584a6a12275a8e1c50d202458639c17ca Mon Sep 17 00:00:00 2001 From: Jonathan Eskew Date: Tue, 4 Oct 2022 09:45:02 -0400 Subject: [PATCH 3/6] Use fixed property names in Config.ExperimentalFeaturesEnabled --- .../ExperimentalFeaturesEnabled.cs | 21 +++++++++++++++++++ ...xperimentalFeaturesEnabledConfiguration.cs | 16 -------------- .../Configuration/RootConfiguration.cs | 6 +++--- .../Extensions/JsonElementExtensions.cs | 8 +------ src/Bicep.Core/Features/FeatureProvider.cs | 13 +++++------- 5 files changed, 30 insertions(+), 34 deletions(-) create mode 100644 src/Bicep.Core/Configuration/ExperimentalFeaturesEnabled.cs delete mode 100644 src/Bicep.Core/Configuration/ExperimentalFeaturesEnabledConfiguration.cs diff --git a/src/Bicep.Core/Configuration/ExperimentalFeaturesEnabled.cs b/src/Bicep.Core/Configuration/ExperimentalFeaturesEnabled.cs new file mode 100644 index 00000000000..dac8c8fadbd --- /dev/null +++ b/src/Bicep.Core/Configuration/ExperimentalFeaturesEnabled.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Text.Json; +using Bicep.Core.Extensions; +using Bicep.Core.Json; + +namespace Bicep.Core.Configuration; + +public record ExperimentalFeaturesEnabled( + bool? SymbolicNameCodegen, + bool? Imports, + bool? ResourceTypedParamsAndOutputs, + bool? SourceMapping, + bool? ParamsFiles) +{ + public static ExperimentalFeaturesEnabled Bind(JsonElement element, string? configurationPath) + => element.ToNonNullObject(); + + public void WriteTo(Utf8JsonWriter writer) => JsonElementFactory.CreateElement(this).WriteTo(writer); +} diff --git a/src/Bicep.Core/Configuration/ExperimentalFeaturesEnabledConfiguration.cs b/src/Bicep.Core/Configuration/ExperimentalFeaturesEnabledConfiguration.cs deleted file mode 100644 index 4e1356b4dfe..00000000000 --- a/src/Bicep.Core/Configuration/ExperimentalFeaturesEnabledConfiguration.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Immutable; -using System.Text.Json; - -namespace Bicep.Core.Configuration; - -public class ExperimentalFeaturesEnabledConfiguration : ConfigurationSection> -{ - public ExperimentalFeaturesEnabledConfiguration(JsonElement data) : base(data.EnumerateObject() - .ToImmutableDictionary(p => p.Name, p => p.Value.GetBoolean(), StringComparer.OrdinalIgnoreCase)) {} - - public bool? IsEnabled(string featureName) => Data.TryGetValue(featureName, out var value) ? value : default; -} diff --git a/src/Bicep.Core/Configuration/RootConfiguration.cs b/src/Bicep.Core/Configuration/RootConfiguration.cs index 4826a2e7a85..fda58b22808 100644 --- a/src/Bicep.Core/Configuration/RootConfiguration.cs +++ b/src/Bicep.Core/Configuration/RootConfiguration.cs @@ -23,7 +23,7 @@ public RootConfiguration( ModuleAliasesConfiguration moduleAliases, AnalyzersConfiguration analyzers, string? cacheRootDirectory, - ExperimentalFeaturesEnabledConfiguration experimentalFeaturesEnabled, + ExperimentalFeaturesEnabled experimentalFeaturesEnabled, string? configurationPath, IEnumerable? diagnosticBuilders) { @@ -42,7 +42,7 @@ public static RootConfiguration Bind(JsonElement element, string? configurationP var moduleAliases = ModuleAliasesConfiguration.Bind(element.GetProperty(ModuleAliasesKey), configurationPath); var analyzers = new AnalyzersConfiguration(element.GetProperty(AnalyzersKey)); var cacheRootDirectory = element.TryGetProperty(CacheRootDirectoryKey, out var e) ? e.GetString() : default; - var experimentalFeaturesEnabled = new ExperimentalFeaturesEnabledConfiguration(element.GetProperty(ExperimentalFeaturesEnabledKey)); + var experimentalFeaturesEnabled = ExperimentalFeaturesEnabled.Bind(element.GetProperty(ExperimentalFeaturesEnabledKey), configurationPath); return new(cloud, moduleAliases, analyzers, cacheRootDirectory, experimentalFeaturesEnabled, configurationPath, diagnosticBuilders); } @@ -55,7 +55,7 @@ public static RootConfiguration Bind(JsonElement element, string? configurationP public string? CacheRootDirectory { get; } - public ExperimentalFeaturesEnabledConfiguration ExperimentalFeaturesEnabled { get; } + public ExperimentalFeaturesEnabled ExperimentalFeaturesEnabled { get; } public string? ConfigurationPath { get; } diff --git a/src/Bicep.Core/Extensions/JsonElementExtensions.cs b/src/Bicep.Core/Extensions/JsonElementExtensions.cs index d61a976bbd3..a59a484d4ee 100644 --- a/src/Bicep.Core/Extensions/JsonElementExtensions.cs +++ b/src/Bicep.Core/Extensions/JsonElementExtensions.cs @@ -29,13 +29,7 @@ public static T ToNonNullObject(this JsonElement element, JsonSerializerOptio { options ??= DefaultDeserializeOptions; - var bufferWriter = new ArrayBufferWriter(); - using (var writer = new Utf8JsonWriter(bufferWriter)) - { - element.WriteTo(writer); - } - - return JsonSerializer.Deserialize(bufferWriter.WrittenSpan, options) ?? + return JsonSerializer.Deserialize(element, options) ?? throw new JsonException($"Expected deserialized value of \"{element}\" to be non-null."); } diff --git a/src/Bicep.Core/Features/FeatureProvider.cs b/src/Bicep.Core/Features/FeatureProvider.cs index 3e871f75c76..80a7cb32fca 100644 --- a/src/Bicep.Core/Features/FeatureProvider.cs +++ b/src/Bicep.Core/Features/FeatureProvider.cs @@ -20,20 +20,17 @@ public FeatureProvider(RootConfiguration configuration) public bool RegistryEnabled => true; - public bool SymbolicNameCodegenEnabled => LookupFeatureFlag("symbolicNameCodegen", false); + public bool SymbolicNameCodegenEnabled => configuration.ExperimentalFeaturesEnabled.SymbolicNameCodegen ?? false; - public bool ImportsEnabled => LookupFeatureFlag("imports", false); + public bool ImportsEnabled => configuration.ExperimentalFeaturesEnabled.Imports ?? false; - public bool ResourceTypedParamsAndOutputsEnabled => LookupFeatureFlag("resourceTypedParamsAndOutputs", false); + public bool ResourceTypedParamsAndOutputsEnabled => configuration.ExperimentalFeaturesEnabled.ResourceTypedParamsAndOutputs ?? false; public string AssemblyVersion => ThisAssembly.AssemblyFileVersion; - public bool SourceMappingEnabled => LookupFeatureFlag("sourceMapping", false); + public bool SourceMappingEnabled => configuration.ExperimentalFeaturesEnabled.SourceMapping ?? false; - public bool ParamsFilesEnabled => LookupFeatureFlag("paramsFiles", false); - - private bool LookupFeatureFlag(string featureName, bool defaultValue) - => configuration.ExperimentalFeaturesEnabled.IsEnabled(featureName) is bool enabled ? enabled : defaultValue; + public bool ParamsFilesEnabled => configuration.ExperimentalFeaturesEnabled.ParamsFiles ?? false; public static bool TracingEnabled => ReadBooleanEnvVar("BICEP_TRACING_ENABLED", defaultValue: false); From 5670a87657122f914bc62c08f75eb93ed075ecc8 Mon Sep 17 00:00:00 2001 From: Jonathan Eskew Date: Tue, 4 Oct 2022 10:56:14 -0400 Subject: [PATCH 4/6] Cleanup diff --- .../RootCommandTests.cs | 21 ++++++++++++------- .../ModuleTests.cs | 10 ++++----- .../ApiVersions/ApiVersionProviderFactory.cs | 7 ++----- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs b/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs index 0015110dd1d..13ebfbad773 100644 --- a/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs +++ b/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs @@ -1,13 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Bicep.Core.Features; using Bicep.Core.UnitTests.Assertions; -using Bicep.Core.UnitTests; using FluentAssertions; using FluentAssertions.Execution; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; -using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace Bicep.Cli.IntegrationTests @@ -15,9 +14,6 @@ namespace Bicep.Cli.IntegrationTests [TestClass] public class RootCommandTests : TestBase { - [NotNull] - public TestContext? TestContext { get; set; } - [TestMethod] public async Task Build_WithWrongArgs_ShouldFail_WithExpectedErrorMessage() { @@ -51,7 +47,10 @@ public async Task BicepVersionShouldPrintVersionInformation() [TestMethod] public async Task BicepHelpShouldPrintHelp() { - var settings = CreateDefaultSettings() with { Features = BicepTestConstants.CreateFeatureProvider(TestContext, registryEnabled: true) }; + var featuresMock = Repository.Create(); + featuresMock.Setup(m => m.RegistryEnabled).Returns(true); + + var settings = CreateDefaultSettings() with { Features = featuresMock.Object }; var (output, error, result) = await Bicep(settings, "--help"); @@ -131,7 +130,10 @@ public async Task BicepThirdPartyNoticesShouldPrintNotices() [TestMethod] public async Task BicepHelpShouldIncludePublishWhenRegistryEnabled() { - var settings = CreateDefaultSettings() with { Features = BicepTestConstants.CreateFeatureProvider(TestContext, registryEnabled: true) }; + var featuresMock = Repository.Create(); + featuresMock.Setup(m => m.RegistryEnabled).Returns(true); + + var settings = CreateDefaultSettings() with { Features = featuresMock.Object }; var (output, error, result) = await Bicep(settings, "--help"); @@ -152,7 +154,10 @@ public async Task BicepHelpShouldIncludePublishWhenRegistryEnabled() [TestMethod] public async Task BicepHelpShouldNotIncludePublishWhenRegistryDisabled() { - var settings = CreateDefaultSettings() with { Features = BicepTestConstants.CreateFeatureProvider(TestContext, registryEnabled: false) }; + var featuresMock = Repository.Create(); + featuresMock.Setup(m => m.RegistryEnabled).Returns(false); + + var settings = CreateDefaultSettings() with { Features = featuresMock.Object }; var (output, error, result) = await Bicep(settings, "--help"); diff --git a/src/Bicep.Core.IntegrationTests/ModuleTests.cs b/src/Bicep.Core.IntegrationTests/ModuleTests.cs index 9f2cab94442..87e3d0fa1fa 100644 --- a/src/Bicep.Core.IntegrationTests/ModuleTests.cs +++ b/src/Bicep.Core.IntegrationTests/ModuleTests.cs @@ -236,10 +236,9 @@ param inputb string SetupFileReaderMock(mockFileResolver, mainFileUri, mainFileContents, null); mockFileResolver.Setup(x => x.TryResolveFilePath(mainFileUri, "modulea.bicep")).Returns((Uri?)null); - var featureManager = BicepTestConstants.FeatureProviderFactory; - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(mockFileResolver.Object, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, featureManager, ConfigurationManager), ConfigurationManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(mockFileResolver.Object, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, ConfigurationManager), ConfigurationManager); - var compilation = new Compilation(featureManager, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingBuilder.Build(mockFileResolver.Object, dispatcher, new Workspace(), mainFileUri), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingBuilder.Build(mockFileResolver.Object, dispatcher, new Workspace(), mainFileUri), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); var (success, diagnosticsByFile) = GetSuccessAndDiagnosticsByFile(compilation); diagnosticsByFile[mainFileUri].Should().HaveDiagnostics(new[] { @@ -380,10 +379,9 @@ param inputb string SetupFileReaderMock(mockFileResolver, moduleAUri, null, x => x.ErrorOccurredReadingFile("Mock read failure!")); mockFileResolver.Setup(x => x.TryResolveFilePath(mainUri, "modulea.bicep")).Returns(moduleAUri); - var featureManager = BicepTestConstants.FeatureProviderFactory; - var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(mockFileResolver.Object, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, featureManager, ConfigurationManager), ConfigurationManager); + var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(mockFileResolver.Object, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, ConfigurationManager), ConfigurationManager); - var compilation = new Compilation(featureManager, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingBuilder.Build(mockFileResolver.Object, dispatcher, new Workspace(), mainUri), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); + var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingBuilder.Build(mockFileResolver.Object, dispatcher, new Workspace(), mainUri), ConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, LinterAnalyzer); var (success, diagnosticsByFile) = GetSuccessAndDiagnosticsByFile(compilation); diagnosticsByFile[mainUri].Should().HaveDiagnostics(new[] { diff --git a/src/Bicep.Core/Analyzers/Linter/ApiVersions/ApiVersionProviderFactory.cs b/src/Bicep.Core/Analyzers/Linter/ApiVersions/ApiVersionProviderFactory.cs index b7554cd74ac..5167d82edf9 100644 --- a/src/Bicep.Core/Analyzers/Linter/ApiVersions/ApiVersionProviderFactory.cs +++ b/src/Bicep.Core/Analyzers/Linter/ApiVersions/ApiVersionProviderFactory.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; -using System.Runtime.CompilerServices; using Bicep.Core.Features; using Bicep.Core.Semantics.Namespaces; @@ -9,7 +8,6 @@ namespace Bicep.Core.Analyzers.Linter.ApiVersions; public class ApiVersionProviderFactory : IApiVersionProviderFactory { - private readonly ConditionalWeakTable apiVersionProviderCache = new(); private readonly IFeatureProviderFactory featureProviderFactory; private readonly INamespaceProvider namespaceProvider; @@ -19,7 +17,6 @@ public ApiVersionProviderFactory(IFeatureProviderFactory featureProviderFactory, this.namespaceProvider = namespaceProvider; } - public IApiVersionProvider GetApiVersionProvider(Uri templateUri) - => apiVersionProviderCache.GetValue(featureProviderFactory.GetFeatureProvider(templateUri), - features => new ApiVersionProvider(features, namespaceProvider)); + public IApiVersionProvider GetApiVersionProvider(Uri templateUri) + => new ApiVersionProvider(featureProviderFactory.GetFeatureProvider(templateUri), namespaceProvider); } From 91c89f25091f8a738d8ae560fd612af926e32848 Mon Sep 17 00:00:00 2001 From: Jonathan Eskew Date: Tue, 4 Oct 2022 11:08:52 -0400 Subject: [PATCH 5/6] Fix failing tests --- .../RootCommandTests.cs | 30 +++-------------- .../ConfigurationManagerTests.cs | 32 ++++++++++++++++--- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs b/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs index 13ebfbad773..d55622cf363 100644 --- a/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs +++ b/src/Bicep.Cli.IntegrationTests/RootCommandTests.cs @@ -128,32 +128,10 @@ public async Task BicepThirdPartyNoticesShouldPrintNotices() } [TestMethod] - public async Task BicepHelpShouldIncludePublishWhenRegistryEnabled() - { - var featuresMock = Repository.Create(); - featuresMock.Setup(m => m.RegistryEnabled).Returns(true); - - var settings = CreateDefaultSettings() with { Features = featuresMock.Object }; - - var (output, error, result) = await Bicep(settings, "--help"); - - result.Should().Be(0); - error.Should().BeEmpty(); - - output.Should().NotBeEmpty(); - output.Should().ContainAll( - "publish", - "Publishes", - "registry", - "reference", - "azurecr.io", - "br", - "--target"); - } - - [TestMethod] - public async Task BicepHelpShouldNotIncludePublishWhenRegistryDisabled() + public async Task BicepHelpShouldAlwaysIncludePublish() { + // disable registry to ensure `bicep --help` is not consulting the feature provider before + // preparing the help text (as features can only be determined when an input file is specified) var featuresMock = Repository.Create(); featuresMock.Setup(m => m.RegistryEnabled).Returns(false); @@ -165,7 +143,7 @@ public async Task BicepHelpShouldNotIncludePublishWhenRegistryDisabled() error.Should().BeEmpty(); output.Should().NotBeEmpty(); - output.Should().NotContainAny( + output.Should().ContainAll( "publish", "Publishes", "registry", diff --git a/src/Bicep.Core.UnitTests/Configuration/ConfigurationManagerTests.cs b/src/Bicep.Core.UnitTests/Configuration/ConfigurationManagerTests.cs index 4aede226745..a7af0e6eedc 100644 --- a/src/Bicep.Core.UnitTests/Configuration/ConfigurationManagerTests.cs +++ b/src/Bicep.Core.UnitTests/Configuration/ConfigurationManagerTests.cs @@ -95,7 +95,13 @@ public void GetBuiltInConfiguration_NoParameter_ReturnsBuiltInConfigurationWithA } } }, - ""experimentalFeaturesEnabled"": {} + ""experimentalFeaturesEnabled"": { + ""symbolicNameCodegen"": null, + ""imports"": null, + ""resourceTypedParamsAndOutputs"": null, + ""sourceMapping"": null, + ""paramsFiles"": null + } }"); } @@ -146,7 +152,13 @@ public void GetBuiltInConfiguration_DisableAllAnalyzers_ReturnsBuiltInConfigurat } }, ""analyzers"": {}, - ""experimentalFeaturesEnabled"": {} + ""experimentalFeaturesEnabled"": { + ""symbolicNameCodegen"": null, + ""imports"": null, + ""resourceTypedParamsAndOutputs"": null, + ""sourceMapping"": null, + ""paramsFiles"": null + } }"); } @@ -223,7 +235,13 @@ public void GetBuiltInConfiguration_DisableAnalyzers_ReturnsBuiltInConfiguration } } }, - ""experimentalFeaturesEnabled"": {} + ""experimentalFeaturesEnabled"": { + ""symbolicNameCodegen"": null, + ""imports"": null, + ""resourceTypedParamsAndOutputs"": null, + ""sourceMapping"": null, + ""paramsFiles"": null + } }"); } @@ -437,7 +455,7 @@ public void GetConfiguration_ValidCustomConfiguration_OverridesBuiltInConfigurat }, ""cacheRootDirectory"": ""/home/username/.bicep/cache"", ""experimentalFeaturesEnabled"": { - ""registry"": false + ""imports"": true } }" }); @@ -522,7 +540,11 @@ public void GetConfiguration_ValidCustomConfiguration_OverridesBuiltInConfigurat }, ""cacheRootDirectory"": ""/home/username/.bicep/cache"", ""experimentalFeaturesEnabled"": { - ""registry"": false + ""symbolicNameCodegen"": null, + ""imports"": true, + ""resourceTypedParamsAndOutputs"": null, + ""sourceMapping"": null, + ""paramsFiles"": null } }"); } From f8e1774d760c07e055a578c9d26ae8cd4d68a617 Mon Sep 17 00:00:00 2001 From: Jonathan Eskew Date: Tue, 4 Oct 2022 16:52:32 -0400 Subject: [PATCH 6/6] Update bicepconfig.json schema --- .../schemas/bicepconfig.schema.json | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/vscode-bicep/schemas/bicepconfig.schema.json b/src/vscode-bicep/schemas/bicepconfig.schema.json index 37161aaea76..e98efd6e864 100644 --- a/src/vscode-bicep/schemas/bicepconfig.schema.json +++ b/src/vscode-bicep/schemas/bicepconfig.schema.json @@ -551,6 +551,31 @@ } } } + }, + "cacheRootDirectory": { + "type": "string", + "description": "The directory in which Bicep may cache templates downloaded from remote registries" + }, + "experimentalFeaturesEnabled": { + "type": "object", + "description": "The experimental Bicep features that should be enabled or disabled for templates using this configuration file", + "properties": { + "symbolicNameCodegen": { + "type": "boolean" + }, + "imports": { + "type": "boolean" + }, + "resourceTypedParamsAndOutputs": { + "type": "boolean" + }, + "sourceMapping": { + "type": "boolean" + }, + "paramsFiles": { + "type": "boolean" + } + } } } }