diff --git a/src/Bicep.Core.IntegrationTests/MsGraphTypesViaRegistryTests.cs b/src/Bicep.Core.IntegrationTests/MsGraphTypesViaRegistryTests.cs new file mode 100644 index 00000000000..be89094d87f --- /dev/null +++ b/src/Bicep.Core.IntegrationTests/MsGraphTypesViaRegistryTests.cs @@ -0,0 +1,280 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO.Abstractions.TestingHelpers; +using Azure; +using Bicep.Core.Configuration; +using Bicep.Core.Diagnostics; +using Bicep.Core.Registry; +using Bicep.Core.Semantics.Namespaces; +using Bicep.Core.TypeSystem.Providers; +using Bicep.Core.UnitTests; +using Bicep.Core.UnitTests.Assertions; +using Bicep.Core.UnitTests.Mock; +using Bicep.Core.UnitTests.Registry; +using Bicep.Core.UnitTests.Utils; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using RegistryUtils = Bicep.Core.UnitTests.Utils.ContainerRegistryClientFactoryExtensions; + +namespace Bicep.Core.IntegrationTests +{ + + [TestClass] + public class MsGraphTypesViaRegistryTests : TestBase + { + private const string versionV10 = "1.2.3"; + private const string versionBeta = "1.2.3-beta"; + private static readonly string EmptyIndexJsonBeta = $$""" +{ + "resources": {}, + "resourceFunctions": {}, + "settings": { + "name": "MicrosoftGraphBeta", + "version": "{{versionBeta}}", + "isSingleton": false + } +} +"""; + private static readonly string EmptyIndexJsonV10 = $$""" +{ + "resources": {}, + "resourceFunctions": {}, + "settings": { + "name": "MicrosoftGraphV1.0", + "version": "{{versionV10}}", + "isSingleton": false + } +} +"""; + + + private async Task GetServices() + { + var indexJsonBeta = FileHelper.SaveResultFile(TestContext, "types/index-beta.json", EmptyIndexJsonBeta); + var indexJsonV10 = FileHelper.SaveResultFile(TestContext, "types/index-v1.0.json", EmptyIndexJsonV10); + + var cacheRoot = FileHelper.GetUniqueTestOutputPath(TestContext); + Directory.CreateDirectory(cacheRoot); + + var services = new ServiceBuilder() + .WithFeatureOverrides(new(ExtensibilityEnabled: true, CacheRootDirectory: cacheRoot)) + .WithContainerRegistryClientFactory(RegistryHelper.CreateOciClientForMsGraphExtension()); + + await RegistryHelper.PublishMsGraphExtension(services.Build(), indexJsonBeta, "beta", versionBeta); + await RegistryHelper.PublishMsGraphExtension(services.Build(), indexJsonV10, "v1", versionV10); + + return services; + } + + private async Task ServicesWithTestExtensionArtifact(ArtifactRegistryAddress artifactRegistryAddress, BinaryData artifactPayload) + { + (var clientFactory, var blobClients) = RegistryUtils.CreateMockRegistryClients(artifactRegistryAddress.ClientDescriptor()); + + (_, var client) = blobClients.First(); + var configResult = await client.UploadBlobAsync(BinaryData.FromString("{}")); + var blobResult = await client.UploadBlobAsync(artifactPayload); + var manifest = BicepTestConstants.GetBicepExtensionManifest(blobResult.Value, configResult.Value); + await client.SetManifestAsync(manifest, artifactRegistryAddress.ExtensionVersion); + + var cacheRoot = FileHelper.GetUniqueTestOutputPath(TestContext); + Directory.CreateDirectory(cacheRoot); + + return new ServiceBuilder() + .WithFeatureOverrides(new(ExtensibilityEnabled: true, CacheRootDirectory: cacheRoot)) + .WithContainerRegistryClientFactory(clientFactory); + } + + [TestMethod] + [DynamicData(nameof(ArtifactRegistryCorruptedPackageNegativeTestScenarios), DynamicDataSourceType.Method)] + public async Task Bicep_compiler_handles_corrupted_extension_package_gracefully( + BinaryData payload, + string innerErrorMessage) + { + // ARRANGE + var testArtifactAddress = new ArtifactRegistryAddress("biceptestdf.azurecr.io", "bicep/extensions/microsoftgraph/beta", "0.0.0-corruptpng"); + + var services = await ServicesWithTestExtensionArtifact(testArtifactAddress, payload); + + // ACT + var result = await CompilationHelper.RestoreAndCompile(services, @$" + extension '{testArtifactAddress.ToSpecificationString(':')}' + "); + + // ASSERT + result.Should().NotGenerateATemplate(); + result.Should().HaveDiagnostics([ + ("BCP396", DiagnosticLevel.Error, """The referenced extension types artifact has been published with malformed content.""") + ]); + } + + public record ArtifactRegistryAddress(string RegistryAddress, string RepositoryPath, string ExtensionVersion) + { + public string ToSpecificationString(char delim) => $"br:{RegistryAddress}/{RepositoryPath}{delim}{ExtensionVersion}"; + + public (string, string) ClientDescriptor() => (RegistryAddress, RepositoryPath); + } + + [TestMethod] + [DynamicData(nameof(ArtifactRegistryAddressNegativeTestScenarios), DynamicDataSourceType.Method)] + public async Task Repository_not_found_in_registry( + ArtifactRegistryAddress artifactRegistryAddress, + Exception exceptionToThrow, + IEnumerable<(string, DiagnosticLevel, string)> expectedDiagnostics) + { + // ARRANGE + // mock the blob client to throw the expected exception + var mockBlobClient = StrictMock.Of(); + mockBlobClient.Setup(m => m.GetManifestAsync(It.IsAny(), It.IsAny())).ThrowsAsync(exceptionToThrow); + + // mock the registry client to return the mock blob client + var containerRegistryFactoryBuilder = new TestContainerRegistryClientFactoryBuilder(); + containerRegistryFactoryBuilder.RegisterMockRepositoryBlobClient( + artifactRegistryAddress.RegistryAddress, + artifactRegistryAddress.RepositoryPath, + mockBlobClient.Object); + + var services = new ServiceBuilder() + .WithFeatureOverrides(new(ExtensibilityEnabled: true)) + .WithContainerRegistryClientFactory(containerRegistryFactoryBuilder.Build().clientFactory); + + // ACT + var result = await CompilationHelper.RestoreAndCompile(services, @$" + extension '{artifactRegistryAddress.ToSpecificationString(':')}' + "); + + // ASSERT + result.Should().NotGenerateATemplate(); + result.Should().HaveDiagnostics(expectedDiagnostics); + } + + public static IEnumerable ArtifactRegistryAddressNegativeTestScenarios() + { + // constants + const string placeholderExtensionVersion = "0.0.0-placeholder"; + + // unresolvable host registry. For example if DNS is down or unresponsive + const string unreachableRegistryAddress = "unknown.registry.azurecr.io"; + const string NoSuchHostMessage = $" (No such host is known. ({unreachableRegistryAddress}:443))"; + var AggregateExceptionMessage = $"Retry failed after 4 tries. Retry settings can be adjusted in ClientOptions.Retry or by configuring a custom retry policy in ClientOptions.RetryPolicy.{string.Concat(Enumerable.Repeat(NoSuchHostMessage, 4))}"; + var unreachable = new ArtifactRegistryAddress(unreachableRegistryAddress, "bicep/extensions/microsoftgraph/beta", placeholderExtensionVersion); + yield return new object[] { + unreachable, + new AggregateException(AggregateExceptionMessage), + new (string, DiagnosticLevel, string)[]{ + ("BCP192", DiagnosticLevel.Error, @$"Unable to restore the artifact with reference ""{unreachable.ToSpecificationString(':')}"": {AggregateExceptionMessage}") + }, + }; + + // manifest not found is thrown when the repository address is not registered and/or the version doesn't exist in the registry + const string NotFoundMessage = "The artifact does not exist in the registry."; + var withoutRepo = new ArtifactRegistryAddress(LanguageConstants.BicepPublicMcrRegistry, "unknown/path/microsoftgraph/beta", placeholderExtensionVersion); + yield return new object[] { + withoutRepo, + new RequestFailedException(404, NotFoundMessage), + new (string, DiagnosticLevel, string)[]{ + ("BCP192", DiagnosticLevel.Error, $@"Unable to restore the artifact with reference ""{withoutRepo.ToSpecificationString(':')}"": {NotFoundMessage}") + }, + }; + } + + public static IEnumerable ArtifactRegistryCorruptedPackageNegativeTestScenarios() + { + // Scenario: When OciTypeLoader.FromDisk() throws, the exception is exposed as a diagnostic + // Some cases covered by this test are: + // - Artifact layer payload is not a GZip compressed + // - Artifact layer payload is a GZip compressedbut is not composed of Tar entries + yield return new object[] + { + BinaryData.FromString("This is a NOT GZip compressed data"), + "The archive entry was compressed using an unsupported compression method.", + }; + + // Scenario: Artifact layer payload is missing an "index.json" + yield return new object[] + { + ThirdPartyTypeHelper.GetTypesTgzBytesFromFiles( + ("unknown.json", "{}")), + "The path: index.json was not found in artifact contents" + }; + + // Scenario: "index.json" is not valid JSON + yield return new object[] + { + ThirdPartyTypeHelper.GetTypesTgzBytesFromFiles( + ("index.json", """{"INVALID_JSON": 777""")), + "'7' is an invalid end of a number. Expected a delimiter. Path: $.INVALID_JSON | LineNumber: 0 | BytePositionInLine: 20." + }; + + // Scenario: "index.json" with malformed or missing required data + yield return new object[] + { + ThirdPartyTypeHelper.GetTypesTgzBytesFromFiles( + ("index.json", """{ "UnexpectedMember": false}""")), + "Value cannot be null. (Parameter 'source')" + }; + } + + [TestMethod] + public async Task External_MsGraph_namespace_can_be_loaded_from_configuration() + { + var services = await GetServices(); + + services = services.WithConfigurationPatch(c => c.WithExtensions($$""" + { + "az": "builtin:", + "msGraphBeta": "br:{{LanguageConstants.BicepPublicMcrRegistry}}/bicep/extensions/microsoftgraph/beta:{{versionBeta}}", + "msGraphV1": "br:{{LanguageConstants.BicepPublicMcrRegistry}}/bicep/extensions/microsoftgraph/v1:{{versionV10}}" + } + """)); + + var result = await CompilationHelper.RestoreAndCompile(services, ("main.bicep", @$" + extension msGraphBeta + extension msGraphV1 + ")); + + result.Should().GenerateATemplate(); + } + + [TestMethod] + public async Task BuiltIn_MsGraph_namespace_can_be_loaded_from_configuration() + { + var services = await GetServices(); + var result = await CompilationHelper.RestoreAndCompile(services, ("main.bicep", @$" + extension microsoftGraph + ")); + + result.Should().GenerateATemplate(); + } + + [TestMethod] + public async Task MsGraph_namespace_can_be_loaded_dynamically_using_extension_configuration() + { + //ARRANGE + var artifactRegistryAddress = new ArtifactRegistryAddress( + "fake.azurecr.io", + "fake/path/microsoftgraph/beta", + "1.0.0-fake"); + var services = await ServicesWithTestExtensionArtifact( + artifactRegistryAddress, + ThirdPartyTypeHelper.GetTypesTgzBytesFromFiles(("index.json", EmptyIndexJsonBeta))); + services = services.WithConfigurationPatch(c => c.WithExtensions($$""" + { + "az": "builtin:", + "msGraphBeta": "{{artifactRegistryAddress.ToSpecificationString(':')}}" + } + """)); + + //ACT + var result = await CompilationHelper.RestoreAndCompile(services, ("main.bicep", @$" + extension msGraphBeta + ")); + + //ASSERT + result.Should().GenerateATemplate(); + result.Template.Should().NotBeNull(); + result.Template.Should().HaveValueAtPath("$.imports.MicrosoftGraphBeta.version", versionBeta); + } + } +} diff --git a/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs b/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs index e67ebce491d..9374fd4c533 100644 --- a/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs +++ b/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs @@ -164,6 +164,18 @@ public static async Task PublishAzExtension(IDependencyHelper services, string p await PublishExtensionToRegistryAsync(services, pathToIndexJson, $"br:{LanguageConstants.BicepPublicMcrRegistry}/{repository}:{version}"); } + public static async Task PublishMsGraphExtension(IDependencyHelper services, string pathToIndexJson, string repoVersion, string extensionVersion) + { + var repository = "bicep/extensions/microsoftgraph/" + repoVersion; + await PublishExtensionToRegistryAsync(services, pathToIndexJson, $"br:{LanguageConstants.BicepPublicMcrRegistry}/{repository}:{extensionVersion}"); + } + public static IContainerRegistryClientFactory CreateOciClientForAzExtension() => CreateMockRegistryClients((LanguageConstants.BicepPublicMcrRegistry, $"bicep/extensions/az")).factoryMock; + + public static IContainerRegistryClientFactory CreateOciClientForMsGraphExtension() + => CreateMockRegistryClients( + (LanguageConstants.BicepPublicMcrRegistry, $"bicep/extensions/microsoftgraph/beta"), + (LanguageConstants.BicepPublicMcrRegistry, $"bicep/extensions/microsoftgraph/v1") + ).factoryMock; } diff --git a/src/Bicep.Core/Semantics/Namespaces/MicrosoftGraphNamespaceType.cs b/src/Bicep.Core/Semantics/Namespaces/MicrosoftGraphNamespaceType.cs index ab9b459731a..1f22779c690 100644 --- a/src/Bicep.Core/Semantics/Namespaces/MicrosoftGraphNamespaceType.cs +++ b/src/Bicep.Core/Semantics/Namespaces/MicrosoftGraphNamespaceType.cs @@ -1,24 +1,37 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections.Immutable; +using System.Diagnostics; +using System.Reflection; +using Azure.Deployments.Core.Definitions.Identifiers; +using Bicep.Core.Diagnostics; +using Bicep.Core.Intermediate; +using Bicep.Core.Registry; +using Bicep.Core.TypeSystem; using Bicep.Core.TypeSystem.Providers; using Bicep.Core.TypeSystem.Providers.MicrosoftGraph; using Bicep.Core.TypeSystem.Types; +using Bicep.Core.Workspaces; +using Microsoft.Graph.Bicep.Types; +using static Bicep.Core.TypeSystem.Providers.ThirdParty.ThirdPartyResourceTypeLoader; namespace Bicep.Core.Semantics.Namespaces { public static class MicrosoftGraphNamespaceType { public const string BuiltInName = "microsoftGraph"; + public const string TemplateExtensionName = "MicrosoftGraph"; + public const string BicepExtensionBetaName = "MicrosoftGraphBeta"; + public const string BicepExtensionV10Name = "MicrosoftGraphV1.0"; private static readonly Lazy TypeProviderLazy = new(() => new MicrosoftGraphResourceTypeProvider(new MicrosoftGraphResourceTypeLoader())); public static NamespaceSettings Settings { get; } = new( - IsSingleton: true, + IsSingleton: false, BicepExtensionName: BuiltInName, ConfigurationType: null, - TemplateExtensionName: "MicrosoftGraph", + TemplateExtensionName: TemplateExtensionName, TemplateExtensionVersion: "1.0.0"); public static NamespaceType Create(string aliasName) @@ -32,5 +45,34 @@ public static NamespaceType Create(string aliasName) ImmutableArray.Empty, TypeProviderLazy.Value); } + + public static NamespaceType Create(string? aliasName, IResourceTypeProvider resourceTypeProvider, ArtifactReference? artifact) + { + if (resourceTypeProvider is MicrosoftGraphResourceTypeProvider microsoftGraphProvider && + microsoftGraphProvider.GetNamespaceConfiguration() is NamespaceConfiguration namespaceConfig) + { + return new NamespaceType( + aliasName ?? namespaceConfig.Name, + new NamespaceSettings( + IsSingleton: namespaceConfig.IsSingleton, + BicepExtensionName: namespaceConfig.Name, + ConfigurationType: namespaceConfig.ConfigurationObject, + TemplateExtensionName: TemplateExtensionName, + TemplateExtensionVersion: namespaceConfig.Version), + ImmutableArray.Empty, + ImmutableArray.Empty, + ImmutableArray.Empty, + ImmutableArray.Empty, + resourceTypeProvider, + artifact); + } + + throw new ArgumentException("Invalid resource type provider or namespace config for Microsoft Graph resource."); + } + + public static bool ShouldUseLoader(string? typeSettingName) + { + return typeSettingName == TemplateExtensionName || typeSettingName == BicepExtensionBetaName || typeSettingName == BicepExtensionV10Name; + } } } diff --git a/src/Bicep.Core/Semantics/Namespaces/NamespaceProvider.cs b/src/Bicep.Core/Semantics/Namespaces/NamespaceProvider.cs index abc410958ac..a5efbc29043 100644 --- a/src/Bicep.Core/Semantics/Namespaces/NamespaceProvider.cs +++ b/src/Bicep.Core/Semantics/Namespaces/NamespaceProvider.cs @@ -15,6 +15,7 @@ using Bicep.Core.TypeSystem; using Bicep.Core.TypeSystem.Providers; using Bicep.Core.TypeSystem.Providers.Az; +using Bicep.Core.TypeSystem.Providers.MicrosoftGraph; using Bicep.Core.TypeSystem.Providers.ThirdParty; using Bicep.Core.TypeSystem.Types; using Bicep.Core.Workspaces; @@ -201,6 +202,11 @@ private ResultWithDiagnosticBuilder GetNamespaceTypeForArtifact(A return new(AzNamespaceType.Create(aliasName, targetScope, typeProvider, sourceFile.FileKind)); } + if (typeProvider is MicrosoftGraphResourceTypeProvider) + { + return new(MicrosoftGraphNamespaceType.Create(aliasName, typeProvider, artifact.Reference)); + } + return new(ThirdPartyNamespaceType.Create(aliasName, typeProvider, artifact.Reference)); } } diff --git a/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeFactory.cs b/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeFactory.cs index 5fe9c6e6d38..170fc5b658d 100644 --- a/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeFactory.cs +++ b/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeFactory.cs @@ -23,6 +23,13 @@ public ResourceTypeComponents GetResourceType(Azure.Bicep.Types.Concrete.Resourc return new ResourceTypeComponents(resourceTypeReference, ToResourceScope(resourceType.ScopeType), ToResourceScope(resourceType.ReadOnlyScopes), ToResourceFlags(resourceType.Flags), bodyType); } + public TypeSymbol GetObjectType(Azure.Bicep.Types.Concrete.ObjectType objectType) + { + var bodyType = GetTypeSymbol(objectType, false); + + return bodyType; + } + private TypeSymbol GetTypeSymbol(Azure.Bicep.Types.Concrete.TypeBase serializedType, bool isResourceBodyType) => typeCache.GetOrAdd(serializedType, serializedType => ToTypeSymbol(serializedType, isResourceBodyType)); diff --git a/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeLoader.cs b/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeLoader.cs index 6d01cb2dd5e..40d6e2fbecb 100644 --- a/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeLoader.cs +++ b/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeLoader.cs @@ -2,9 +2,11 @@ // Licensed under the MIT License. using System.Collections.Immutable; using Azure.Bicep.Types; +using Azure.Bicep.Types.Index; using Bicep.Core.Resources; using Bicep.Core.TypeSystem.Types; using Microsoft.Graph.Bicep.Types; +using static Bicep.Core.TypeSystem.Providers.ThirdParty.ThirdPartyResourceTypeLoader; namespace Bicep.Core.TypeSystem.Providers.MicrosoftGraph { @@ -13,15 +15,24 @@ public class MicrosoftGraphResourceTypeLoader : IResourceTypeLoader private readonly ITypeLoader typeLoader; private readonly MicrosoftGraphResourceTypeFactory resourceTypeFactory; private readonly ImmutableDictionary availableTypes; + private readonly TypeSettings? typeSettings; + private readonly CrossFileTypeReference? fallbackResourceType; - public MicrosoftGraphResourceTypeLoader() + public MicrosoftGraphResourceTypeLoader(ITypeLoader typeLoader) { - typeLoader = new MicrosoftGraphTypeLoader(); + this.typeLoader = typeLoader; resourceTypeFactory = new MicrosoftGraphResourceTypeFactory(); var indexedTypes = typeLoader.LoadTypeIndex(); availableTypes = indexedTypes.Resources.ToImmutableDictionary( kvp => ResourceTypeReference.Parse(kvp.Key), kvp => kvp.Value); + + typeSettings = indexedTypes.Settings; + fallbackResourceType = indexedTypes.FallbackResourceType; + } + + public MicrosoftGraphResourceTypeLoader() : this(new MicrosoftGraphTypeLoader()) + { } public IEnumerable GetAvailableTypes() @@ -34,5 +45,32 @@ public ResourceTypeComponents LoadType(ResourceTypeReference reference) var serializedResourceType = typeLoader.LoadResourceType(typeLocation); return resourceTypeFactory.GetResourceType(serializedResourceType); } + + public NamespaceConfiguration? LoadNamespaceConfiguration() + { + if (typeSettings == null) + { + throw new ArgumentException($"Please provide the following Settings properties: Name, Version, & IsSingleton."); + } + + ObjectType? configurationType = null; + if (typeSettings.ConfigurationType is { } reference) + { + + if (typeLoader.LoadType(reference) is not Azure.Bicep.Types.Concrete.ObjectType concreteObjectType || + resourceTypeFactory.GetObjectType(concreteObjectType) is not ObjectType objectType) + { + throw new ArgumentException($"Unable to locate resource object type at index {reference.Index} in \"{reference.RelativePath}\" resource"); + } + + configurationType = objectType; + } + + return new( + typeSettings.Name, + typeSettings.Version, + typeSettings.IsSingleton, + configurationType); + } } } diff --git a/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeProvider.cs b/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeProvider.cs index 6fbaa4a9180..beab167a447 100644 --- a/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeProvider.cs +++ b/src/Bicep.Core/TypeSystem/Providers/MicrosoftGraph/MicrosoftGraphResourceTypeProvider.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections.Immutable; +using System.Reflection; using Bicep.Core.Resources; +using Bicep.Core.TypeSystem.Providers.ThirdParty; using Bicep.Core.TypeSystem.Types; namespace Bicep.Core.TypeSystem.Providers.MicrosoftGraph @@ -179,6 +181,9 @@ public bool HasDefinedType(ResourceTypeReference typeReference) public IEnumerable GetAvailableTypes() => availableResourceTypes; - public string Version { get; } = "1.0.0"; + public ThirdPartyResourceTypeLoader.NamespaceConfiguration? GetNamespaceConfiguration() + { + return resourceTypeLoader.LoadNamespaceConfiguration(); + } } } diff --git a/src/Bicep.Core/TypeSystem/Providers/ResourceTypeProviderFactory.cs b/src/Bicep.Core/TypeSystem/Providers/ResourceTypeProviderFactory.cs index f2a85ccecb2..a10caf2b06c 100644 --- a/src/Bicep.Core/TypeSystem/Providers/ResourceTypeProviderFactory.cs +++ b/src/Bicep.Core/TypeSystem/Providers/ResourceTypeProviderFactory.cs @@ -10,6 +10,7 @@ using Bicep.Core.Registry; using Bicep.Core.Semantics.Namespaces; using Bicep.Core.TypeSystem.Providers.Az; +using Bicep.Core.TypeSystem.Providers.MicrosoftGraph; using Bicep.Core.TypeSystem.Providers.ThirdParty; using JetBrains.Annotations; @@ -37,13 +38,18 @@ public ResultWithDiagnosticBuilder GetResourceTypeProvide var typeIndex = typesLoader.LoadTypeIndex(); var useAzLoader = typeIndex.Settings?.Name == AzNamespaceType.Settings.TemplateExtensionName; + var useMsGraphLoader = MicrosoftGraphNamespaceType.ShouldUseLoader(typeIndex.Settings?.Name); if (useAzLoader) { return new(new AzResourceTypeProvider(new AzResourceTypeLoader(typesLoader, typeIndex))); } + else if (useMsGraphLoader) + { + return new(new MicrosoftGraphResourceTypeProvider(new MicrosoftGraphResourceTypeLoader(typesLoader))); + } - return new(new ThirdPartyResourceTypeProvider(new ThirdPartyResourceTypeLoader(typesLoader, typeIndex))); + return new(new ThirdPartyResourceTypeProvider(new ThirdPartyResourceTypeLoader(typesLoader))); } catch (Exception ex) {