From edcade8ecfe879bbc8cbc9c6d558e1416d7dd296 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 22 Jan 2026 13:37:51 -0800 Subject: [PATCH 01/23] Changes for autoentities --- src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index dad3eeb873..4de4e3d786 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -60,6 +60,7 @@ public override Type SqlToCLRType(string sqlType) /// public override async Task PopulateTriggerMetadataForTable(string entityName, string schemaName, string tableName, SourceDefinition sourceDefinition) { + string enumerateEnabledTriggers = SqlQueryBuilder.BuildFetchEnabledTriggersQuery(); Dictionary parameters = new() { From c380bb6b483f885af21be6fa445c12d2a0260be6 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Mon, 26 Jan 2026 15:28:51 -0800 Subject: [PATCH 02/23] Add query and its execution --- src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 4de4e3d786..306e015620 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -382,7 +382,7 @@ protected override async Task GenerateAutoentitiesIntoEntities(IReadOnlyDictiona _runtimeConfigProvider.AddMergedEntitiesToConfig(entities); } - public async Task QueryAutoentitiesAsync(Autoentity autoentity) + public async Task QueryAutoentitiesConfiguration(Autoentity autoentity) { string include = string.Join(",", autoentity.Patterns.Include); string exclude = string.Join(",", autoentity.Patterns.Exclude); From aacecaa76593a7417181c607ba795a49e3a3f89a Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Mon, 26 Jan 2026 21:26:04 -0800 Subject: [PATCH 03/23] Add testing --- src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index c9ce82180b..43d3c647bb 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -19,6 +19,7 @@ using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.Tests.Configuration; using Azure.DataApiBuilder.Service.Tests.SqlTests; +using HotChocolate.Execution.Processing; using Microsoft.AspNetCore.Http; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; From d8b9856abc40f676e883e51c0e0b2cc275167636 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Wed, 28 Jan 2026 16:49:13 -0800 Subject: [PATCH 04/23] Changes based on comments --- src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index 43d3c647bb..c9ce82180b 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -19,7 +19,6 @@ using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.Tests.Configuration; using Azure.DataApiBuilder.Service.Tests.SqlTests; -using HotChocolate.Execution.Processing; using Microsoft.AspNetCore.Http; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; From a8dfa5b7ff2ec070cbef05885d1860336ccf4e6f Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Wed, 28 Jan 2026 17:26:57 -0800 Subject: [PATCH 05/23] Changes based on comments --- .../Services/MetadataProviders/MsSqlMetadataProvider.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 306e015620..a45d93eeca 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -382,7 +382,7 @@ protected override async Task GenerateAutoentitiesIntoEntities(IReadOnlyDictiona _runtimeConfigProvider.AddMergedEntitiesToConfig(entities); } - public async Task QueryAutoentitiesConfiguration(Autoentity autoentity) + public async Task QueryAutoentitiesAsync(Autoentity autoentity) { string include = string.Join(",", autoentity.Patterns.Include); string exclude = string.Join(",", autoentity.Patterns.Exclude); @@ -400,6 +400,11 @@ protected override async Task GenerateAutoentitiesIntoEntities(IReadOnlyDictiona _logger.LogInformation($"Autoentities exclude pattern: {exclude}"); _logger.LogInformation($"Autoentities name pattern: {namePattern}"); + _logger.LogInformation("Query for Autoentities is being executed with the following parameters."); + _logger.LogInformation($"Autoentities include pattern: {include}"); + _logger.LogInformation($"Autoentities exclude pattern: {exclude}"); + _logger.LogInformation($"Autoentities name pattern: {namePattern}"); + JsonArray? resultArray = await QueryExecutor.ExecuteQueryAsync( sqltext: getAutoentitiesQuery, parameters: parameters, From 35d335f1b1f53a552cc171da5eceefbaa43dbe03 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 29 Jan 2026 17:19:58 -0800 Subject: [PATCH 06/23] Added changes based on comments --- src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index a45d93eeca..4de4e3d786 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -400,11 +400,6 @@ protected override async Task GenerateAutoentitiesIntoEntities(IReadOnlyDictiona _logger.LogInformation($"Autoentities exclude pattern: {exclude}"); _logger.LogInformation($"Autoentities name pattern: {namePattern}"); - _logger.LogInformation("Query for Autoentities is being executed with the following parameters."); - _logger.LogInformation($"Autoentities include pattern: {include}"); - _logger.LogInformation($"Autoentities exclude pattern: {exclude}"); - _logger.LogInformation($"Autoentities name pattern: {namePattern}"); - JsonArray? resultArray = await QueryExecutor.ExecuteQueryAsync( sqltext: getAutoentitiesQuery, parameters: parameters, From 5cf342468122cbcb46e535741d074d633c1b0019 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 29 Jan 2026 17:48:50 -0800 Subject: [PATCH 07/23] Fix formatting issues --- src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 4de4e3d786..dad3eeb873 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -60,7 +60,6 @@ public override Type SqlToCLRType(string sqlType) /// public override async Task PopulateTriggerMetadataForTable(string entityName, string schemaName, string tableName, SourceDefinition sourceDefinition) { - string enumerateEnabledTriggers = SqlQueryBuilder.BuildFetchEnabledTriggersQuery(); Dictionary parameters = new() { From 4dd7c0efc5e103374d938fba05a4a085a06a4677 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 5 Feb 2026 09:52:16 -0800 Subject: [PATCH 08/23] Generate in-memory entities --- src/Core/Services/MetadataProviders/SqlMetadataProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index c7bc023142..8ce7700549 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -310,6 +310,7 @@ public string GetEntityName(string graphQLType) public async Task InitializeAsync() { System.Diagnostics.Stopwatch timer = System.Diagnostics.Stopwatch.StartNew(); + if (GetDatabaseType() == DatabaseType.MSSQL) { await GenerateAutoentitiesIntoEntities(Autoentities); From 687c6c87449eb4cbf735d339c0710f112528882b Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Tue, 10 Feb 2026 17:01:11 -0800 Subject: [PATCH 09/23] Changes to generate autoentities as entities --- src/Core/Configurations/RuntimeConfigValidator.cs | 2 +- src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs | 1 + src/Service/Startup.cs | 2 -- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Core/Configurations/RuntimeConfigValidator.cs b/src/Core/Configurations/RuntimeConfigValidator.cs index f5112844da..a0d788fea6 100644 --- a/src/Core/Configurations/RuntimeConfigValidator.cs +++ b/src/Core/Configurations/RuntimeConfigValidator.cs @@ -677,7 +677,7 @@ public void ValidateEntityConfiguration(RuntimeConfig runtimeConfig) // Stores the unique rest paths configured for different entities present in the config. HashSet restPathsForEntities = new(); - foreach ((string entityName, Entity entity) in runtimeConfig.Entities) + foreach ((string entityName, Entity entity) in runtimeConfig.Entities ?? Enumerable.Empty>()) { if (runtimeConfig.IsRestEnabled && entity.Rest is not null && entity.Rest.Enabled) { diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index dad3eeb873..f26bdd6711 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -6,6 +6,7 @@ using System.Net; using System.Text.Json; using System.Text.Json.Nodes; +using Azure.DataApiBuilder.Auth; using Azure.DataApiBuilder.Config.DatabasePrimitives; using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Core.Configurations; diff --git a/src/Service/Startup.cs b/src/Service/Startup.cs index f9e7043440..05f920a2c0 100644 --- a/src/Service/Startup.cs +++ b/src/Service/Startup.cs @@ -1174,13 +1174,11 @@ private async Task PerformOnConfigChangeAsync(IApplicationBuilder app) // Now that the configuration has been set, perform validation of the runtime config // itself. - // TODO: Task #3131. Need to change validation so that the validation of entities is done after the autoentities are generated and added with the regular entitites. runtimeConfigValidator.ValidateConfigProperties(); if (runtimeConfig.IsDevelopmentMode()) { // Running only in developer mode to ensure fast and smooth startup in production. - // TODO: Task #3131. Need to change validation so that the validation of entities is done after the autoentities are generated and added with the regular entitites. runtimeConfigValidator.ValidatePermissionsInConfig(runtimeConfig); } From f44e5ab17d6f3539ffca697437914043ff431dde Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 12 Feb 2026 14:36:42 -0800 Subject: [PATCH 10/23] Add new testing --- src/Service.Tests/Configuration/ConfigurationTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 6781c81675..ee07cbc538 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.ComponentModel; using System.IdentityModel.Tokens.Jwt; using System.IO; using System.IO.Abstractions; From ce3bb0991dfc1acb98ddc32f9bbefd84fe1f09de Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 12 Feb 2026 17:21:42 -0800 Subject: [PATCH 11/23] Fix grammar errors --- src/Core/Configurations/RuntimeConfigValidator.cs | 2 +- src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs | 1 - src/Service.Tests/Configuration/ConfigurationTests.cs | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Core/Configurations/RuntimeConfigValidator.cs b/src/Core/Configurations/RuntimeConfigValidator.cs index a0d788fea6..f5112844da 100644 --- a/src/Core/Configurations/RuntimeConfigValidator.cs +++ b/src/Core/Configurations/RuntimeConfigValidator.cs @@ -677,7 +677,7 @@ public void ValidateEntityConfiguration(RuntimeConfig runtimeConfig) // Stores the unique rest paths configured for different entities present in the config. HashSet restPathsForEntities = new(); - foreach ((string entityName, Entity entity) in runtimeConfig.Entities ?? Enumerable.Empty>()) + foreach ((string entityName, Entity entity) in runtimeConfig.Entities) { if (runtimeConfig.IsRestEnabled && entity.Rest is not null && entity.Rest.Enabled) { diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index f26bdd6711..dad3eeb873 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -6,7 +6,6 @@ using System.Net; using System.Text.Json; using System.Text.Json.Nodes; -using Azure.DataApiBuilder.Auth; using Azure.DataApiBuilder.Config.DatabasePrimitives; using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Core.Configurations; diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index ee07cbc538..6781c81675 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; -using System.ComponentModel; using System.IdentityModel.Tokens.Jwt; using System.IO; using System.IO.Abstractions; From bcd0a524c94248d23b70a4235203a7f3a2d15e10 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Fri, 20 Feb 2026 11:43:16 -0800 Subject: [PATCH 12/23] Changes to ensure autoentities work with multiple data sources --- src/Config/ObjectModel/RuntimeConfig.cs | 11 +++++++++++ .../MetadataProviders/MsSqlMetadataProvider.cs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Config/ObjectModel/RuntimeConfig.cs b/src/Config/ObjectModel/RuntimeConfig.cs index 0684040f85..2f41469321 100644 --- a/src/Config/ObjectModel/RuntimeConfig.cs +++ b/src/Config/ObjectModel/RuntimeConfig.cs @@ -641,6 +641,17 @@ private void CheckEntityNamePresent(string entityName) } } + private void CheckAutoentityNamePresent(string autoentityName) + { + if (!_autoentityNameToDataSourceName.ContainsKey(autoentityName)) + { + throw new DataApiBuilderException( + message: $"{autoentityName} is not a valid autoentity.", + statusCode: HttpStatusCode.NotFound, + subStatusCode: DataApiBuilderException.SubStatusCodes.EntityNotFound); + } + } + private void SetupDataSourcesUsed() { SqlDataSourceUsed = _dataSourceNameToDataSource.Values.Any diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index dad3eeb873..a2b6c90ac1 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -352,7 +352,7 @@ protected override async Task GenerateAutoentitiesIntoEntities(IReadOnlyDictiona // Add the generated entity to the linking entities dictionary. // This allows the entity to be processed later during metadata population. - if (!entities.TryAdd(entityName, generatedEntity) || !runtimeConfig.TryAddGeneratedAutoentityNameToDataSourceName(entityName, autoentityName)) + if s!entities.TryAdd(entityName, generatedEntity) || !runtimeConfig.TryAddGeneratedAutoentityNameToDataSourceName(entityName, autoentityName)) { throw new DataApiBuilderException( message: $"Entity with name '{entityName}' already exists. Cannot create new entity from autoentity pattern with definition-name '{autoentityName}'.", From b0b426e71b67bff09ec4a4942a449b6aa02b993e Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Fri, 20 Feb 2026 19:17:39 -0800 Subject: [PATCH 13/23] Move validation of entities --- .../Configurations/RuntimeConfigValidator.cs | 13 +-- .../CosmosSqlMetadataProvider.cs | 15 +++- .../MetadataProviderFactory.cs | 13 +-- .../MsSqlMetadataProvider.cs | 3 +- .../MySqlMetadataProvider.cs | 3 +- .../PostgreSqlMetadataProvider.cs | 3 +- .../MetadataProviders/SqlMetadataProvider.cs | 20 +++++ .../Configuration/ConfigurationTests.cs | 86 +++++++++++++++++++ src/Service/Startup.cs | 6 -- 9 files changed, 135 insertions(+), 27 deletions(-) diff --git a/src/Core/Configurations/RuntimeConfigValidator.cs b/src/Core/Configurations/RuntimeConfigValidator.cs index f5112844da..8eb5980bd2 100644 --- a/src/Core/Configurations/RuntimeConfigValidator.cs +++ b/src/Core/Configurations/RuntimeConfigValidator.cs @@ -96,18 +96,6 @@ public void ValidateConfigProperties() ValidateLoggerFilters(runtimeConfig); ValidateAzureLogAnalyticsAuth(runtimeConfig); ValidateFileSinkPath(runtimeConfig); - - // Running these graphQL validations only in development mode to ensure - // fast startup of engine in production mode. - if (runtimeConfig.IsDevelopmentMode()) - { - ValidateEntityConfiguration(runtimeConfig); - - if (runtimeConfig.IsGraphQLEnabled) - { - ValidateEntitiesDoNotGenerateDuplicateQueriesOrMutation(runtimeConfig.DataSource.DatabaseType, runtimeConfig.Entities); - } - } } /// @@ -499,6 +487,7 @@ public async Task ValidateEntitiesMetadata(RuntimeConfig runtimeConfig, ILoggerF // Only used for validation so we don't need the handler which is for hot reload scenarios. MetadataProviderFactory metadataProviderFactory = new( runtimeConfigProvider: _runtimeConfigProvider, + runtimeConfigValidator: this, queryManagerFactory: queryManagerFactory, logger: loggerFactory.CreateLogger(), fileSystem: _fileSystem, diff --git a/src/Core/Services/MetadataProviders/CosmosSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/CosmosSqlMetadataProvider.cs index 61ffeeab09..e59df29f41 100644 --- a/src/Core/Services/MetadataProviders/CosmosSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/CosmosSqlMetadataProvider.cs @@ -55,7 +55,7 @@ public class CosmosSqlMetadataProvider : ISqlMetadataProvider public List SqlMetadataExceptions { get; private set; } = new(); - public CosmosSqlMetadataProvider(RuntimeConfigProvider runtimeConfigProvider, IFileSystem fileSystem) + public CosmosSqlMetadataProvider(RuntimeConfigProvider runtimeConfigProvider, RuntimeConfigValidator runtimeConfigValidator, IFileSystem fileSystem) { RuntimeConfig runtimeConfig = runtimeConfigProvider.GetConfig(); _fileSystem = fileSystem; @@ -76,6 +76,19 @@ public CosmosSqlMetadataProvider(RuntimeConfigProvider runtimeConfigProvider, IF subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization); } + if (_isDevelopmentMode) + { + runtimeConfigValidator.ValidateEntityConfiguration(runtimeConfig); + + if (runtimeConfig.IsGraphQLEnabled) + { + runtimeConfigValidator.ValidateEntitiesDoNotGenerateDuplicateQueriesOrMutation(runtimeConfig.DataSource.DatabaseType, runtimeConfig.Entities); + } + + // Running only in developer mode to ensure fast and smooth startup in production. + runtimeConfigValidator.ValidatePermissionsInConfig(runtimeConfig); + } + _cosmosDb = cosmosDb; ParseSchemaGraphQLDocument(); diff --git a/src/Core/Services/MetadataProviders/MetadataProviderFactory.cs b/src/Core/Services/MetadataProviders/MetadataProviderFactory.cs index 66112fce21..6fe20969ed 100644 --- a/src/Core/Services/MetadataProviders/MetadataProviderFactory.cs +++ b/src/Core/Services/MetadataProviders/MetadataProviderFactory.cs @@ -19,6 +19,7 @@ public class MetadataProviderFactory : IMetadataProviderFactory { private readonly IDictionary _metadataProviders; private readonly RuntimeConfigProvider _runtimeConfigProvider; + private readonly RuntimeConfigValidator _runtimeConfigValidator; private readonly IAbstractQueryManagerFactory _queryManagerFactory; private readonly ILogger _logger; private readonly IFileSystem _fileSystem; @@ -26,6 +27,7 @@ public class MetadataProviderFactory : IMetadataProviderFactory public MetadataProviderFactory( RuntimeConfigProvider runtimeConfigProvider, + RuntimeConfigValidator runtimeConfigValidator, IAbstractQueryManagerFactory queryManagerFactory, ILogger logger, IFileSystem fileSystem, @@ -34,6 +36,7 @@ public MetadataProviderFactory( { handler?.Subscribe(METADATA_PROVIDER_FACTORY_ON_CONFIG_CHANGED, OnConfigChanged); _runtimeConfigProvider = runtimeConfigProvider; + _runtimeConfigValidator = runtimeConfigValidator; _queryManagerFactory = queryManagerFactory; _logger = logger; _fileSystem = fileSystem; @@ -48,11 +51,11 @@ private void ConfigureMetadataProviders() { ISqlMetadataProvider metadataProvider = dataSource.DatabaseType switch { - DatabaseType.CosmosDB_NoSQL => new CosmosSqlMetadataProvider(_runtimeConfigProvider, _fileSystem), - DatabaseType.MSSQL => new MsSqlMetadataProvider(_runtimeConfigProvider, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly), - DatabaseType.DWSQL => new MsSqlMetadataProvider(_runtimeConfigProvider, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly), - DatabaseType.PostgreSQL => new PostgreSqlMetadataProvider(_runtimeConfigProvider, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly), - DatabaseType.MySQL => new MySqlMetadataProvider(_runtimeConfigProvider, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly), + DatabaseType.CosmosDB_NoSQL => new CosmosSqlMetadataProvider(_runtimeConfigProvider, _runtimeConfigValidator, _fileSystem), + DatabaseType.MSSQL => new MsSqlMetadataProvider(_runtimeConfigProvider, _runtimeConfigValidator, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly), + DatabaseType.DWSQL => new MsSqlMetadataProvider(_runtimeConfigProvider, _runtimeConfigValidator, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly), + DatabaseType.PostgreSQL => new PostgreSqlMetadataProvider(_runtimeConfigProvider, _runtimeConfigValidator, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly), + DatabaseType.MySQL => new MySqlMetadataProvider(_runtimeConfigProvider, _runtimeConfigValidator, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly), _ => throw new NotSupportedException(dataSource.DatabaseTypeNotSupportedMessage), }; diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index a2b6c90ac1..4f0f967510 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -33,11 +33,12 @@ public class MsSqlMetadataProvider : public MsSqlMetadataProvider( RuntimeConfigProvider runtimeConfigProvider, + RuntimeConfigValidator runtimeConfigValidator, IAbstractQueryManagerFactory queryManagerFactory, ILogger logger, string dataSourceName, bool isValidateOnly = false) - : base(runtimeConfigProvider, queryManagerFactory, logger, dataSourceName, isValidateOnly) + : base(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory, logger, dataSourceName, isValidateOnly) { _runtimeConfigProvider = runtimeConfigProvider; } diff --git a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs index 99336180d5..26098c2d15 100644 --- a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs @@ -23,11 +23,12 @@ public class MySqlMetadataProvider : SqlMetadataProvider logger, string dataSourceName, bool isValidateOnly = false) - : base(runtimeConfigProvider, queryManagerFactory, logger, dataSourceName, isValidateOnly) + : base(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory, logger, dataSourceName, isValidateOnly) { try { diff --git a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs index ecd65b3d95..0d43d0efbc 100644 --- a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs @@ -22,11 +22,12 @@ public class PostgreSqlMetadataProvider : public PostgreSqlMetadataProvider( RuntimeConfigProvider runtimeConfigProvider, + RuntimeConfigValidator runtimeConfigValidator, IAbstractQueryManagerFactory queryManagerFactory, ILogger logger, string dataSourceName, bool isValidateOnly = false) - : base(runtimeConfigProvider, queryManagerFactory, logger, dataSourceName, isValidateOnly) + : base(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory, logger, dataSourceName, isValidateOnly) { } diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 8ce7700549..f1b51db86c 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -77,6 +77,8 @@ public abstract class SqlMetadataProvider : private RuntimeConfigProvider _runtimeConfigProvider; + private RuntimeConfigValidator _runtimeConfigValidator; + private Dictionary> EntityBackingColumnsToExposedNames { get; } = new(); private Dictionary> EntityExposedNamesToBackingColumnNames { get; } = new(); @@ -108,6 +110,7 @@ private void HandleOrRecordException(Exception e) public SqlMetadataProvider( RuntimeConfigProvider runtimeConfigProvider, + RuntimeConfigValidator runtimeConfigValidator, IAbstractQueryManagerFactory engineFactory, ILogger logger, string dataSourceName, @@ -115,6 +118,7 @@ public SqlMetadataProvider( { RuntimeConfig runtimeConfig = runtimeConfigProvider.GetConfig(); _runtimeConfigProvider = runtimeConfigProvider; + _runtimeConfigValidator = runtimeConfigValidator; _dataSourceName = dataSourceName; _databaseType = runtimeConfig.GetDataSourceFromDataSourceName(dataSourceName).DatabaseType; _logger = logger; @@ -316,6 +320,22 @@ public async Task InitializeAsync() await GenerateAutoentitiesIntoEntities(Autoentities); } + // Running these graphQL validations only in development mode to ensure + // fast startup of engine in production mode. + RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig(); + if (runtimeConfig.IsDevelopmentMode()) + { + _runtimeConfigValidator.ValidateEntityConfiguration(runtimeConfig); + + if (runtimeConfig.IsGraphQLEnabled) + { + _runtimeConfigValidator.ValidateEntitiesDoNotGenerateDuplicateQueriesOrMutation(runtimeConfig.DataSource.DatabaseType, runtimeConfig.Entities); + } + + // Running only in developer mode to ensure fast and smooth startup in production. + _runtimeConfigValidator.ValidatePermissionsInConfig(runtimeConfig); + } + GenerateDatabaseObjectForEntities(); if (_isValidateOnly) { diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 6781c81675..501ec3e92b 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -5434,6 +5434,92 @@ public async Task TestAutoentitiesAreGeneratedIntoEntities(bool useEntities, int } } + [TestCategory(TestCategory.MSSQL)] + [DataTestMethod] + [DataRow("Publisher", "uniqueSingularPublisher", "uniquePluralPublishers", "/unique/publisher", DisplayName = "DAB ")] + [DataRow(false, 2, DisplayName = "Test Autoentities without additional entities")] + public void ValidateAutoentityGenerationConflicts(string entityName, string singular, string plural, string path) + { + // Arrange + Entity publisherEntity = new( + Source: new("publishers", EntitySourceType.Table, null, null), + Fields: null, + Rest: null, + GraphQL: new(Singular: singular, Plural: plural), + Permissions: new[] { GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, + Relationships: null, + Mappings: null); + + Dictionary entityMap = new() + { + { entityName, publisherEntity } + }; + + Dictionary autoentityMap = new() + { + { + "PublisherAutoEntity", new Autoentity( + Patterns: new AutoentityPatterns( + Include: new[] { "%" }, + Exclude: null, + Name: null + ), + Template: new AutoentityTemplate( + Rest: new EntityRestOptions( + Enabled: true, + Path: path), + GraphQL: new EntityGraphQLOptions( + Singular: string.Empty, + Plural: string.Empty, + Enabled: true + ), + Health: null, + Cache: null + ), + Permissions: new[] { GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) } + ) + } + }; + + // Create DataSource for MSSQL connection + DataSource dataSource = new(DatabaseType.MSSQL, + GetConnectionStringFromEnvironmentConfig(environment: TestCategory.MSSQL), Options: null); + + // Build complete runtime configuration with autoentities + RuntimeConfig configuration = new( + Schema: "TestAutoentitiesSchema", + DataSource: dataSource, + Runtime: new( + Rest: new(Enabled: true), + GraphQL: new(Enabled: true), + Mcp: new(Enabled: false), + Host: new( + Cors: null, + Authentication: new Config.ObjectModel.AuthenticationOptions( + Provider: nameof(EasyAuthType.StaticWebApps), + Jwt: null + ) + ) + ), + Entities: new(entityMap), + Autoentities: new RuntimeAutoentities(autoentityMap) + ); + + File.WriteAllText(CUSTOM_CONFIG_FILENAME, configuration.ToJson()); + + string[] args = new[] { $"--ConfigFileName={CUSTOM_CONFIG_FILENAME}" }; + + try + { + using TestServer server = new(Program.CreateWebHostBuilder(args)); + } + catch (Exception ex) + { + string exceptionMessage = "holi"; + Assert.AreEqual(exceptionMessage, ex.Message); + } + } + /// /// Tests the behavior of GraphQL queries in non-hosted mode when the depth limit is explicitly set to -1 or null. /// Setting the depth limit to -1 is intended to disable the depth limit check, allowing queries of any depth. diff --git a/src/Service/Startup.cs b/src/Service/Startup.cs index 05f920a2c0..c24d8be8a8 100644 --- a/src/Service/Startup.cs +++ b/src/Service/Startup.cs @@ -1176,12 +1176,6 @@ private async Task PerformOnConfigChangeAsync(IApplicationBuilder app) runtimeConfigValidator.ValidateConfigProperties(); - if (runtimeConfig.IsDevelopmentMode()) - { - // Running only in developer mode to ensure fast and smooth startup in production. - runtimeConfigValidator.ValidatePermissionsInConfig(runtimeConfig); - } - IMetadataProviderFactory sqlMetadataProviderFactory = app.ApplicationServices.GetRequiredService(); await sqlMetadataProviderFactory.InitializeAsync(); From 923a7afcd6e3188687607f6db64ff6038511f2a9 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Fri, 20 Feb 2026 19:23:08 -0800 Subject: [PATCH 14/23] Change test --- src/Service.Tests/Configuration/ConfigurationTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 501ec3e92b..e6c989788f 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -5437,7 +5437,6 @@ public async Task TestAutoentitiesAreGeneratedIntoEntities(bool useEntities, int [TestCategory(TestCategory.MSSQL)] [DataTestMethod] [DataRow("Publisher", "uniqueSingularPublisher", "uniquePluralPublishers", "/unique/publisher", DisplayName = "DAB ")] - [DataRow(false, 2, DisplayName = "Test Autoentities without additional entities")] public void ValidateAutoentityGenerationConflicts(string entityName, string singular, string plural, string path) { // Arrange From e521b085f2a396a04ed6e18d59629f7c053d1238 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Fri, 20 Feb 2026 23:36:19 -0800 Subject: [PATCH 15/23] Add testing --- src/Service.Tests/Configuration/ConfigurationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index e6c989788f..28cee4300e 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -5436,7 +5436,7 @@ public async Task TestAutoentitiesAreGeneratedIntoEntities(bool useEntities, int [TestCategory(TestCategory.MSSQL)] [DataTestMethod] - [DataRow("Publisher", "uniqueSingularPublisher", "uniquePluralPublishers", "/unique/publisher", DisplayName = "DAB ")] + [DataRow("Publisher", "uniqueSingularPublisher", "uniquePluralPublishers", "/unique/publisher", DisplayName = "DAB Autoentities")] public void ValidateAutoentityGenerationConflicts(string entityName, string singular, string plural, string path) { // Arrange From bbf5ceb34cd2205d834c084d064f9564a947c6ab Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Sun, 22 Feb 2026 20:36:55 -0800 Subject: [PATCH 16/23] Refactor code --- .../Configurations/RuntimeConfigValidator.cs | 22 +++++++++++++++++++ .../CosmosSqlMetadataProvider.cs | 13 +---------- .../MetadataProviders/SqlMetadataProvider.cs | 13 +---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Core/Configurations/RuntimeConfigValidator.cs b/src/Core/Configurations/RuntimeConfigValidator.cs index 8eb5980bd2..9c0a782be1 100644 --- a/src/Core/Configurations/RuntimeConfigValidator.cs +++ b/src/Core/Configurations/RuntimeConfigValidator.cs @@ -1583,4 +1583,26 @@ private static bool IsLoggerFilterValid(string loggerFilter) return false; } + + /// + /// Checks that all of the entities created with the Entities and Autoentities properties + /// are valid by not having unique paths for both REST and GraphQL, that there are no duplicate + /// Queries or Mutation entities, and ensure the semantic correctness of all the entities. + /// + /// The runtime configuration. + public void ValidateEntityAndAutoentityConfigurations(RuntimeConfig runtimeConfig) + { + if (runtimeConfig.IsDevelopmentMode()) + { + ValidateEntityConfiguration(runtimeConfig); + + if (runtimeConfig.IsGraphQLEnabled) + { + ValidateEntitiesDoNotGenerateDuplicateQueriesOrMutation(runtimeConfig.DataSource.DatabaseType, runtimeConfig.Entities); + } + + // Running only in developer mode to ensure fast and smooth startup in production. + ValidatePermissionsInConfig(runtimeConfig); + } + } } diff --git a/src/Core/Services/MetadataProviders/CosmosSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/CosmosSqlMetadataProvider.cs index e59df29f41..02d3226af0 100644 --- a/src/Core/Services/MetadataProviders/CosmosSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/CosmosSqlMetadataProvider.cs @@ -76,18 +76,7 @@ public CosmosSqlMetadataProvider(RuntimeConfigProvider runtimeConfigProvider, Ru subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization); } - if (_isDevelopmentMode) - { - runtimeConfigValidator.ValidateEntityConfiguration(runtimeConfig); - - if (runtimeConfig.IsGraphQLEnabled) - { - runtimeConfigValidator.ValidateEntitiesDoNotGenerateDuplicateQueriesOrMutation(runtimeConfig.DataSource.DatabaseType, runtimeConfig.Entities); - } - - // Running only in developer mode to ensure fast and smooth startup in production. - runtimeConfigValidator.ValidatePermissionsInConfig(runtimeConfig); - } + runtimeConfigValidator.ValidateEntityAndAutoentityConfigurations(runtimeConfig); _cosmosDb = cosmosDb; ParseSchemaGraphQLDocument(); diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index f1b51db86c..19efbe1e26 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -323,18 +323,7 @@ public async Task InitializeAsync() // Running these graphQL validations only in development mode to ensure // fast startup of engine in production mode. RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig(); - if (runtimeConfig.IsDevelopmentMode()) - { - _runtimeConfigValidator.ValidateEntityConfiguration(runtimeConfig); - - if (runtimeConfig.IsGraphQLEnabled) - { - _runtimeConfigValidator.ValidateEntitiesDoNotGenerateDuplicateQueriesOrMutation(runtimeConfig.DataSource.DatabaseType, runtimeConfig.Entities); - } - - // Running only in developer mode to ensure fast and smooth startup in production. - _runtimeConfigValidator.ValidatePermissionsInConfig(runtimeConfig); - } + _runtimeConfigValidator.ValidateEntityAndAutoentityConfigurations(runtimeConfig); GenerateDatabaseObjectForEntities(); if (_isValidateOnly) From 9e62a654130f9726e0f15a1e3ae6d546a2b31709 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Wed, 25 Feb 2026 12:41:05 -0800 Subject: [PATCH 17/23] Fix Tests --- .../Configuration/ConfigurationTests.cs | 40 ++++++++++++++----- src/Service.Tests/CosmosTests/TestBase.cs | 3 +- .../GraphQLBuilder/MultiSourceBuilderTests.cs | 3 +- .../MultipleMutationBuilderTests.cs | 4 ++ src/Service.Tests/SqlTests/SqlTestBase.cs | 5 +++ .../UnitTests/SqlMetadataProviderUnitTests.cs | 16 ++++++-- 6 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 28cee4300e..244ee17167 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -3680,9 +3680,10 @@ public void ValidateGraphQLSchemaForCircularReference(string schema) }); FileSystemRuntimeConfigLoader loader = new(fileSystem); RuntimeConfigProvider provider = new(loader); + Mock runtimeConfigValidator = new(); DataApiBuilderException exception = - Assert.ThrowsException(() => new CosmosSqlMetadataProvider(provider, fileSystem)); + Assert.ThrowsException(() => new CosmosSqlMetadataProvider(provider, runtimeConfigValidator.Object, fileSystem)); Assert.AreEqual("Circular reference detected in the provided GraphQL schema for entity 'Character'.", exception.Message); Assert.AreEqual(HttpStatusCode.InternalServerError, exception.StatusCode); Assert.AreEqual(DataApiBuilderException.SubStatusCodes.ErrorInInitialization, exception.SubStatusCode); @@ -3732,9 +3733,10 @@ type Planet @model(name:""PlanetAlias"") { }); FileSystemRuntimeConfigLoader loader = new(fileSystem); RuntimeConfigProvider provider = new(loader); + Mock runtimeConfigValidator = new(); DataApiBuilderException exception = - Assert.ThrowsException(() => new CosmosSqlMetadataProvider(provider, fileSystem)); + Assert.ThrowsException(() => new CosmosSqlMetadataProvider(provider, runtimeConfigValidator.Object, fileSystem)); Assert.AreEqual("The entity 'Character' was not found in the runtime config.", exception.Message); Assert.AreEqual(HttpStatusCode.ServiceUnavailable, exception.StatusCode); Assert.AreEqual(DataApiBuilderException.SubStatusCodes.ConfigValidationError, exception.SubStatusCode); @@ -5436,14 +5438,17 @@ public async Task TestAutoentitiesAreGeneratedIntoEntities(bool useEntities, int [TestCategory(TestCategory.MSSQL)] [DataTestMethod] - [DataRow("Publisher", "uniqueSingularPublisher", "uniquePluralPublishers", "/unique/publisher", DisplayName = "DAB Autoentities")] - public void ValidateAutoentityGenerationConflicts(string entityName, string singular, string plural, string path) + [DataRow("publishers", "uniqueSingularPublisher", "uniquePluralPublishers", "/unique/publisher", "Entity with name 'publishers' already exists. Cannot create new entity from autoentity pattern with definition-name 'PublisherAutoEntity'.", DisplayName = "Autoentities fail due to entity name")] + [DataRow("UniquePublisher", "publishers", "uniquePluralPublishers", "/unique/publisher", "Entity publishers generates queries/mutation that already exist", DisplayName = "Autoentities fail due to graphql singular type")] + [DataRow("UniquePublisher", "uniqueSingularPublisher", "publishers", "/unique/publisher", "Entity publishers generates queries/mutation that already exist", DisplayName = "Autoentities fail due to graphql plural type")] + [DataRow("UniquePublisher", "uniqueSingularPublisher", "uniquePluralPublishers", "/publishers", "The rest path: publishers specified for entity: publishers is already used by another entity.", DisplayName = "Autoentities fail due to rest path")] + public async Task ValidateAutoentityGenerationConflicts(string entityName, string singular, string plural, string path, string exceptionMessage) { // Arrange Entity publisherEntity = new( Source: new("publishers", EntitySourceType.Table, null, null), Fields: null, - Rest: null, + Rest: new(Path: path), GraphQL: new(Singular: singular, Plural: plural), Permissions: new[] { GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, Relationships: null, @@ -5459,14 +5464,13 @@ public void ValidateAutoentityGenerationConflicts(string entityName, string sing { "PublisherAutoEntity", new Autoentity( Patterns: new AutoentityPatterns( - Include: new[] { "%" }, + Include: new[] { "%publishers%" }, Exclude: null, Name: null ), Template: new AutoentityTemplate( Rest: new EntityRestOptions( - Enabled: true, - Path: path), + Enabled: true), GraphQL: new EntityGraphQLOptions( Singular: string.Empty, Plural: string.Empty, @@ -5493,6 +5497,7 @@ public void ValidateAutoentityGenerationConflicts(string entityName, string sing GraphQL: new(Enabled: true), Mcp: new(Enabled: false), Host: new( + Mode: HostMode.Development, Cors: null, Authentication: new Config.ObjectModel.AuthenticationOptions( Provider: nameof(EasyAuthType.StaticWebApps), @@ -5506,15 +5511,28 @@ public void ValidateAutoentityGenerationConflicts(string entityName, string sing File.WriteAllText(CUSTOM_CONFIG_FILENAME, configuration.ToJson()); - string[] args = new[] { $"--ConfigFileName={CUSTOM_CONFIG_FILENAME}" }; + IFileSystem fileSystem = new FileSystem(); + ; + FileSystemRuntimeConfigLoader configLoader = new(fileSystem) + { + RuntimeConfig = configuration + }; + + RuntimeConfigProvider configProvider = new(configLoader); + + Mock> loggerValidator = new(); + RuntimeConfigValidator configValidator = new(configProvider, fileSystem, loggerValidator.Object); + + LoggerFactory loggerFactory = new(); try { - using TestServer server = new(Program.CreateWebHostBuilder(args)); + await configValidator.ValidateEntitiesMetadata(configuration, loggerFactory); + + Assert.Fail("It is expected for DAB to fail due to entities not containing unique parameters."); } catch (Exception ex) { - string exceptionMessage = "holi"; Assert.AreEqual(exceptionMessage, ex.Message); } } diff --git a/src/Service.Tests/CosmosTests/TestBase.cs b/src/Service.Tests/CosmosTests/TestBase.cs index 8617534776..175071956d 100644 --- a/src/Service.Tests/CosmosTests/TestBase.cs +++ b/src/Service.Tests/CosmosTests/TestBase.cs @@ -158,7 +158,8 @@ protected WebApplicationFactory SetupTestApplicationFactory() FileSystemRuntimeConfigLoader loader = new(fileSystem); RuntimeConfigProvider provider = new(loader); - ISqlMetadataProvider cosmosSqlMetadataProvider = new CosmosSqlMetadataProvider(provider, fileSystem); + Mock runtimeConfigValidator = new(); + ISqlMetadataProvider cosmosSqlMetadataProvider = new CosmosSqlMetadataProvider(provider, runtimeConfigValidator.Object, fileSystem); Mock metadataProviderFactory = new(); metadataProviderFactory.Setup(x => x.GetMetadataProvider(It.IsAny())).Returns(cosmosSqlMetadataProvider); diff --git a/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs index 7c40dafcd6..75d4d92d1f 100644 --- a/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs @@ -46,11 +46,12 @@ public async Task CosmosSchemaBuilderTestAsync() RuntimeConfigProvider provider = new(loader); + Mock runtimeConfigValidator = new(); Mock queryManagerfactory = new(); Mock queryEngineFactory = new(); Mock mutationEngineFactory = new(); Mock> logger = new(); - IMetadataProviderFactory metadataProviderFactory = new MetadataProviderFactory(provider, queryManagerfactory.Object, logger.Object, fs, handler: null); + IMetadataProviderFactory metadataProviderFactory = new MetadataProviderFactory(provider, runtimeConfigValidator.Object, queryManagerfactory.Object, logger.Object, fs, handler: null); Mock authResolver = new(); GraphQLSchemaCreator creator = new(provider, queryEngineFactory.Object, mutationEngineFactory.Object, metadataProviderFactory, authResolver.Object); diff --git a/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs index 94665d7c18..7b60a28171 100644 --- a/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs @@ -392,6 +392,9 @@ private static async Task GetGQLSchemaCreator(RuntimeConfi Mock cache = new(); DabCacheService cacheService = new(cache: cache.Object, logger: null, httpContextAccessor: httpContextAccessor.Object); + // Setup mock runtime config validator + Mock runtimeConfigValidator = new(); + // Setup query manager factory. IAbstractQueryManagerFactory queryManagerfactory = new QueryManagerFactory( runtimeConfigProvider: runtimeConfigProvider, @@ -402,6 +405,7 @@ private static async Task GetGQLSchemaCreator(RuntimeConfi // Setup metadata provider factory. IMetadataProviderFactory metadataProviderFactory = new MetadataProviderFactory( runtimeConfigProvider: runtimeConfigProvider, + runtimeConfigValidator: runtimeConfigValidator.Object, queryManagerFactory: queryManagerfactory, logger: metadatProviderLogger.Object, fileSystem: null, diff --git a/src/Service.Tests/SqlTests/SqlTestBase.cs b/src/Service.Tests/SqlTests/SqlTestBase.cs index 16e804f117..7e3f4caadb 100644 --- a/src/Service.Tests/SqlTests/SqlTestBase.cs +++ b/src/Service.Tests/SqlTests/SqlTestBase.cs @@ -279,6 +279,7 @@ protected static void SetUpSQLMetadataProvider(RuntimeConfigProvider runtimeConf _queryManagerFactory = new Mock(); Mock httpContextAccessor = new(); string dataSourceName = runtimeConfigProvider.GetConfig().DefaultDataSourceName; + Mock runtimeConfigValidator = new(); switch (DatabaseEngine) { case TestCategory.POSTGRESQL: @@ -297,6 +298,7 @@ protected static void SetUpSQLMetadataProvider(RuntimeConfigProvider runtimeConf _sqlMetadataProvider = new PostgreSqlMetadataProvider( runtimeConfigProvider, + runtimeConfigValidator.Object, _queryManagerFactory.Object, _sqlMetadataLogger, dataSourceName); @@ -317,6 +319,7 @@ protected static void SetUpSQLMetadataProvider(RuntimeConfigProvider runtimeConf _sqlMetadataProvider = new MsSqlMetadataProvider( runtimeConfigProvider, + runtimeConfigValidator.Object, _queryManagerFactory.Object, _sqlMetadataLogger, dataSourceName); @@ -337,6 +340,7 @@ protected static void SetUpSQLMetadataProvider(RuntimeConfigProvider runtimeConf _sqlMetadataProvider = new MySqlMetadataProvider( runtimeConfigProvider, + runtimeConfigValidator.Object, _queryManagerFactory.Object, _sqlMetadataLogger, dataSourceName); @@ -357,6 +361,7 @@ protected static void SetUpSQLMetadataProvider(RuntimeConfigProvider runtimeConf _sqlMetadataProvider = new MsSqlMetadataProvider( runtimeConfigProvider, + runtimeConfigValidator.Object, _queryManagerFactory.Object, _sqlMetadataLogger, dataSourceName); diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index c9ce82180b..6a58d799c9 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -129,8 +129,11 @@ public void CheckTablePrefix(string schemaName, string tableName, string expecte queryManagerFactory.Setup(x => x.GetQueryBuilder(It.IsAny())).Returns(queryBuilder); queryManagerFactory.Setup(x => x.GetQueryExecutor(It.IsAny())).Returns(queryExecutor.Object); + Mock runtimeConfigValidator = new(); + SqlMetadataProvider provider = new MsSqlMetadataProvider( runtimeConfigProvider, + runtimeConfigValidator.Object, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName); @@ -215,11 +218,13 @@ private static async Task CheckExceptionForBadConnectionStringHelperAsync(string queryManagerFactory.Setup(x => x.GetQueryBuilder(It.IsAny())).Returns(_queryBuilder); queryManagerFactory.Setup(x => x.GetQueryExecutor(It.IsAny())).Returns(_queryExecutor); + Mock runtimeConfigValidator = new(); + ISqlMetadataProvider sqlMetadataProvider = databaseType switch { - TestCategory.MSSQL => new MsSqlMetadataProvider(runtimeConfigProvider, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), - TestCategory.MYSQL => new MySqlMetadataProvider(runtimeConfigProvider, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), - TestCategory.POSTGRESQL => new PostgreSqlMetadataProvider(runtimeConfigProvider, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), + TestCategory.MSSQL => new MsSqlMetadataProvider(runtimeConfigProvider, runtimeConfigValidator.Object, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), + TestCategory.MYSQL => new MySqlMetadataProvider(runtimeConfigProvider, runtimeConfigValidator.Object, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), + TestCategory.POSTGRESQL => new PostgreSqlMetadataProvider(runtimeConfigProvider, runtimeConfigValidator.Object, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), _ => throw new ArgumentException($"Invalid database type: {databaseType}") }; @@ -480,8 +485,11 @@ public async Task ValidateExceptionForInvalidResultFieldNames(string invalidFiel queryManagerFactory.Setup(x => x.GetQueryBuilder(It.IsAny())).Returns(_queryBuilder); queryManagerFactory.Setup(x => x.GetQueryExecutor(It.IsAny())).Returns(mockQueryExecutor.Object); + Mock runtimeConfigValidator = new(); + ISqlMetadataProvider sqlMetadataProvider = new MsSqlMetadataProvider( runtimeConfigProvider, + runtimeConfigValidator.Object, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName); @@ -598,7 +606,7 @@ private static async Task SetupTestFixtureAndInferMetadata() [DataRow(new string[] { "dbo.%publish%" }, new string[] { }, "{schema}.{object}", new string[] { "publish" }, "")] [DataRow(new string[] { "dbo.%book%" }, new string[] { "dbo.%books%" }, "{schema}_{object}_exclude_books", new string[] { "book" }, "books")] [DataRow(new string[] { "dbo.%book%", "dbo.%publish%" }, new string[] { }, "{object}", new string[] { "book", "publish" }, "")] - [DataRow(new string[] { }, new string[] { "dbo.%book%" }, "{object}", new string[] { "" }, "book")] + [DataRow(new string[] { }, new string[] { "dbo.%book%", "foo.%", "bar.%", "%GQLmappings%" }, "{object}s", new string[] { "" }, "book")] public async Task CheckAutoentitiesQuery(string[] include, string[] exclude, string name, string[] includeObject, string excludeObject) { // Arrange From 18c90d8bf1c69d4de0e65531b2349364c90436f0 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 26 Feb 2026 14:40:38 -0800 Subject: [PATCH 18/23] Fix syntax errors --- src/Config/ObjectModel/RuntimeConfig.cs | 11 ----------- src/Core/Configurations/RuntimeConfigValidator.cs | 2 +- .../MetadataProviders/MsSqlMetadataProvider.cs | 2 +- src/Service.Tests/Configuration/ConfigurationTests.cs | 2 +- .../UnitTests/SqlMetadataProviderUnitTests.cs | 2 +- 5 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/Config/ObjectModel/RuntimeConfig.cs b/src/Config/ObjectModel/RuntimeConfig.cs index 2f41469321..0684040f85 100644 --- a/src/Config/ObjectModel/RuntimeConfig.cs +++ b/src/Config/ObjectModel/RuntimeConfig.cs @@ -641,17 +641,6 @@ private void CheckEntityNamePresent(string entityName) } } - private void CheckAutoentityNamePresent(string autoentityName) - { - if (!_autoentityNameToDataSourceName.ContainsKey(autoentityName)) - { - throw new DataApiBuilderException( - message: $"{autoentityName} is not a valid autoentity.", - statusCode: HttpStatusCode.NotFound, - subStatusCode: DataApiBuilderException.SubStatusCodes.EntityNotFound); - } - } - private void SetupDataSourcesUsed() { SqlDataSourceUsed = _dataSourceNameToDataSource.Values.Any diff --git a/src/Core/Configurations/RuntimeConfigValidator.cs b/src/Core/Configurations/RuntimeConfigValidator.cs index 9c0a782be1..20af5c9911 100644 --- a/src/Core/Configurations/RuntimeConfigValidator.cs +++ b/src/Core/Configurations/RuntimeConfigValidator.cs @@ -1586,7 +1586,7 @@ private static bool IsLoggerFilterValid(string loggerFilter) /// /// Checks that all of the entities created with the Entities and Autoentities properties - /// are valid by not having unique paths for both REST and GraphQL, that there are no duplicate + /// are valid by having unique paths for both REST and GraphQL, that there are no duplicate /// Queries or Mutation entities, and ensure the semantic correctness of all the entities. /// /// The runtime configuration. diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 4f0f967510..4cd9e852c4 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -353,7 +353,7 @@ protected override async Task GenerateAutoentitiesIntoEntities(IReadOnlyDictiona // Add the generated entity to the linking entities dictionary. // This allows the entity to be processed later during metadata population. - if s!entities.TryAdd(entityName, generatedEntity) || !runtimeConfig.TryAddGeneratedAutoentityNameToDataSourceName(entityName, autoentityName)) + if (!entities.TryAdd(entityName, generatedEntity) || !runtimeConfig.TryAddGeneratedAutoentityNameToDataSourceName(entityName, autoentityName)) { throw new DataApiBuilderException( message: $"Entity with name '{entityName}' already exists. Cannot create new entity from autoentity pattern with definition-name '{autoentityName}'.", diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 244ee17167..145c78f762 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -5512,7 +5512,7 @@ public async Task ValidateAutoentityGenerationConflicts(string entityName, strin File.WriteAllText(CUSTOM_CONFIG_FILENAME, configuration.ToJson()); IFileSystem fileSystem = new FileSystem(); - ; + FileSystemRuntimeConfigLoader configLoader = new(fileSystem) { RuntimeConfig = configuration diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index 6a58d799c9..a8d333ad27 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -606,7 +606,7 @@ private static async Task SetupTestFixtureAndInferMetadata() [DataRow(new string[] { "dbo.%publish%" }, new string[] { }, "{schema}.{object}", new string[] { "publish" }, "")] [DataRow(new string[] { "dbo.%book%" }, new string[] { "dbo.%books%" }, "{schema}_{object}_exclude_books", new string[] { "book" }, "books")] [DataRow(new string[] { "dbo.%book%", "dbo.%publish%" }, new string[] { }, "{object}", new string[] { "book", "publish" }, "")] - [DataRow(new string[] { }, new string[] { "dbo.%book%", "foo.%", "bar.%", "%GQLmappings%" }, "{object}s", new string[] { "" }, "book")] + [DataRow(new string[] { }, new string[] { "dbo.%book%" }, "{object}s", new string[] { "" }, "book")] public async Task CheckAutoentitiesQuery(string[] include, string[] exclude, string name, string[] includeObject, string excludeObject) { // Arrange From ef3c23e5b28ee93448693fb253964013a3fc222b Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 26 Feb 2026 15:32:02 -0800 Subject: [PATCH 19/23] Changes based on comments --- .../MsSqlMetadataProvider.cs | 2 +- .../MetadataProviders/SqlMetadataProvider.cs | 23 ++++++++++--------- .../Configuration/ConfigurationTests.cs | 1 - 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 4cd9e852c4..96fa47dcfd 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -375,7 +375,7 @@ protected override async Task GenerateAutoentitiesIntoEntities(IReadOnlyDictiona if (addedEntities == 0) { - _logger.LogWarning($"No new entities were generated from the autoentity {autoentityName} defined in the configuration."); + _logger.LogWarning("No new entities were generated from the autoentity {autoentityName} defined in the configuration.", autoentityName); } } diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 19efbe1e26..a804d5417a 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -315,17 +315,6 @@ public async Task InitializeAsync() { System.Diagnostics.Stopwatch timer = System.Diagnostics.Stopwatch.StartNew(); - if (GetDatabaseType() == DatabaseType.MSSQL) - { - await GenerateAutoentitiesIntoEntities(Autoentities); - } - - // Running these graphQL validations only in development mode to ensure - // fast startup of engine in production mode. - RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig(); - _runtimeConfigValidator.ValidateEntityAndAutoentityConfigurations(runtimeConfig); - - GenerateDatabaseObjectForEntities(); if (_isValidateOnly) { // Currently Validate mode only support single datasource, @@ -342,6 +331,18 @@ public async Task InitializeAsync() } } + if (GetDatabaseType() == DatabaseType.MSSQL) + { + await GenerateAutoentitiesIntoEntities(Autoentities); + } + + // Running these graphQL validations only in development mode to ensure + // fast startup of engine in production mode. + RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig(); + _runtimeConfigValidator.ValidateEntityAndAutoentityConfigurations(runtimeConfig); + + GenerateDatabaseObjectForEntities(); + await PopulateObjectDefinitionForEntities(); GenerateExposedToBackingColumnMapsForEntities(); // When IsLateConfigured is true we are in a hosted scenario and do not reveal primary key information. diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 145c78f762..39f9aec268 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -5528,7 +5528,6 @@ public async Task ValidateAutoentityGenerationConflicts(string entityName, strin try { await configValidator.ValidateEntitiesMetadata(configuration, loggerFactory); - Assert.Fail("It is expected for DAB to fail due to entities not containing unique parameters."); } catch (Exception ex) From 9d30fd9b55aa7be4ce887b2bcaba7fe95966c341 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 26 Feb 2026 16:01:01 -0800 Subject: [PATCH 20/23] Fix test errors by replacing mock with real object --- .../Configuration/ConfigurationTests.cs | 28 ++++++++++++++++--- src/Service.Tests/CosmosTests/TestBase.cs | 8 ++++-- .../GraphQLBuilder/MultiSourceBuilderTests.cs | 7 +++-- .../MultipleMutationBuilderTests.cs | 15 ++++++++-- src/Service.Tests/SqlTests/SqlTestBase.cs | 16 +++++++---- .../UnitTests/SqlMetadataProviderUnitTests.cs | 19 +++++++++---- 6 files changed, 71 insertions(+), 22 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 39f9aec268..90b1e9e5ee 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.Configuration; using System.IdentityModel.Tokens.Jwt; using System.IO; using System.IO.Abstractions; @@ -3680,10 +3681,12 @@ public void ValidateGraphQLSchemaForCircularReference(string schema) }); FileSystemRuntimeConfigLoader loader = new(fileSystem); RuntimeConfigProvider provider = new(loader); - Mock runtimeConfigValidator = new(); + + Mock> loggerValidator = new(); + RuntimeConfigValidator validator = new(provider, fileSystem, loggerValidator.Object); DataApiBuilderException exception = - Assert.ThrowsException(() => new CosmosSqlMetadataProvider(provider, runtimeConfigValidator.Object, fileSystem)); + Assert.ThrowsException(() => new CosmosSqlMetadataProvider(provider, validator, fileSystem)); Assert.AreEqual("Circular reference detected in the provided GraphQL schema for entity 'Character'.", exception.Message); Assert.AreEqual(HttpStatusCode.InternalServerError, exception.StatusCode); Assert.AreEqual(DataApiBuilderException.SubStatusCodes.ErrorInInitialization, exception.SubStatusCode); @@ -3733,10 +3736,12 @@ type Planet @model(name:""PlanetAlias"") { }); FileSystemRuntimeConfigLoader loader = new(fileSystem); RuntimeConfigProvider provider = new(loader); - Mock runtimeConfigValidator = new(); + + Mock> loggerValidator = new(); + RuntimeConfigValidator validator = new(provider, fileSystem, loggerValidator.Object); DataApiBuilderException exception = - Assert.ThrowsException(() => new CosmosSqlMetadataProvider(provider, runtimeConfigValidator.Object, fileSystem)); + Assert.ThrowsException(() => new CosmosSqlMetadataProvider(provider, validator, fileSystem)); Assert.AreEqual("The entity 'Character' was not found in the runtime config.", exception.Message); Assert.AreEqual(HttpStatusCode.ServiceUnavailable, exception.StatusCode); Assert.AreEqual(DataApiBuilderException.SubStatusCodes.ConfigValidationError, exception.SubStatusCode); @@ -5290,6 +5295,12 @@ public async Task TestGraphQLIntrospectionQueriesAreNotImpactedByDepthLimit() } } + /// + /// + /// + /// + /// + /// [TestCategory(TestCategory.MSSQL)] [DataTestMethod] [DataRow(true, 4, DisplayName = "Test Autoentities with additional entities")] @@ -5436,6 +5447,15 @@ public async Task TestAutoentitiesAreGeneratedIntoEntities(bool useEntities, int } } + /// + /// + /// + /// + /// + /// + /// + /// + /// [TestCategory(TestCategory.MSSQL)] [DataTestMethod] [DataRow("publishers", "uniqueSingularPublisher", "uniquePluralPublishers", "/unique/publisher", "Entity with name 'publishers' already exists. Cannot create new entity from autoentity pattern with definition-name 'PublisherAutoEntity'.", DisplayName = "Autoentities fail due to entity name")] diff --git a/src/Service.Tests/CosmosTests/TestBase.cs b/src/Service.Tests/CosmosTests/TestBase.cs index 175071956d..5fe86ff3b1 100644 --- a/src/Service.Tests/CosmosTests/TestBase.cs +++ b/src/Service.Tests/CosmosTests/TestBase.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.IO.Abstractions; using System.IO.Abstractions.TestingHelpers; using System.Linq; @@ -21,6 +22,7 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Azure.Cosmos; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Newtonsoft.Json.Linq; @@ -158,8 +160,10 @@ protected WebApplicationFactory SetupTestApplicationFactory() FileSystemRuntimeConfigLoader loader = new(fileSystem); RuntimeConfigProvider provider = new(loader); - Mock runtimeConfigValidator = new(); - ISqlMetadataProvider cosmosSqlMetadataProvider = new CosmosSqlMetadataProvider(provider, runtimeConfigValidator.Object, fileSystem); + Mock> loggerValidator = new(); + RuntimeConfigValidator validator = new(provider, fileSystem, loggerValidator.Object); + + ISqlMetadataProvider cosmosSqlMetadataProvider = new CosmosSqlMetadataProvider(provider, validator, fileSystem); Mock metadataProviderFactory = new(); metadataProviderFactory.Setup(x => x.GetMetadataProvider(It.IsAny())).Returns(cosmosSqlMetadataProvider); diff --git a/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs index 75d4d92d1f..5efb8f7f61 100644 --- a/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Collections.Generic; +using System.Configuration; using System.IO; using System.IO.Abstractions; using System.IO.Abstractions.TestingHelpers; @@ -46,12 +47,14 @@ public async Task CosmosSchemaBuilderTestAsync() RuntimeConfigProvider provider = new(loader); - Mock runtimeConfigValidator = new(); + Mock> loggerValidator = new(); + RuntimeConfigValidator validator = new(provider, fs, loggerValidator.Object); + Mock queryManagerfactory = new(); Mock queryEngineFactory = new(); Mock mutationEngineFactory = new(); Mock> logger = new(); - IMetadataProviderFactory metadataProviderFactory = new MetadataProviderFactory(provider, runtimeConfigValidator.Object, queryManagerfactory.Object, logger.Object, fs, handler: null); + IMetadataProviderFactory metadataProviderFactory = new MetadataProviderFactory(provider, validator, queryManagerfactory.Object, logger.Object, fs, handler: null); Mock authResolver = new(); GraphQLSchemaCreator creator = new(provider, queryEngineFactory.Object, mutationEngineFactory.Object, metadataProviderFactory, authResolver.Object); diff --git a/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs index 7b60a28171..57ebcf8b90 100644 --- a/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System.Collections.Generic; +using System.Configuration; +using System.IO.Abstractions; using System.Linq; using System.Threading.Tasks; using Azure.DataApiBuilder.Auth; @@ -392,8 +394,15 @@ private static async Task GetGQLSchemaCreator(RuntimeConfi Mock cache = new(); DabCacheService cacheService = new(cache: cache.Object, logger: null, httpContextAccessor: httpContextAccessor.Object); - // Setup mock runtime config validator - Mock runtimeConfigValidator = new(); + // Setup runtime config validator + IFileSystem fileSystem = new FileSystem(); + FileSystemRuntimeConfigLoader configLoader = new(fileSystem) + { + RuntimeConfig = _runtimeConfig + }; + RuntimeConfigProvider configProvider = new(configLoader); + Mock> loggerValidator = new(); + RuntimeConfigValidator configValidator = new(configProvider, fileSystem, loggerValidator.Object); // Setup query manager factory. IAbstractQueryManagerFactory queryManagerfactory = new QueryManagerFactory( @@ -405,7 +414,7 @@ private static async Task GetGQLSchemaCreator(RuntimeConfi // Setup metadata provider factory. IMetadataProviderFactory metadataProviderFactory = new MetadataProviderFactory( runtimeConfigProvider: runtimeConfigProvider, - runtimeConfigValidator: runtimeConfigValidator.Object, + runtimeConfigValidator: configValidator, queryManagerFactory: queryManagerfactory, logger: metadatProviderLogger.Object, fileSystem: null, diff --git a/src/Service.Tests/SqlTests/SqlTestBase.cs b/src/Service.Tests/SqlTests/SqlTestBase.cs index 7e3f4caadb..9b4b38cedf 100644 --- a/src/Service.Tests/SqlTests/SqlTestBase.cs +++ b/src/Service.Tests/SqlTests/SqlTestBase.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.IO; +using System.IO.Abstractions; using System.Net; using System.Net.Http; using System.Net.Http.Json; @@ -13,6 +15,7 @@ using System.Text.Json.Nodes; using System.Threading.Tasks; using Azure.DataApiBuilder.Auth; +using Azure.DataApiBuilder.Config; using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Core.Authorization; using Azure.DataApiBuilder.Core.Configurations; @@ -279,7 +282,10 @@ protected static void SetUpSQLMetadataProvider(RuntimeConfigProvider runtimeConf _queryManagerFactory = new Mock(); Mock httpContextAccessor = new(); string dataSourceName = runtimeConfigProvider.GetConfig().DefaultDataSourceName; - Mock runtimeConfigValidator = new(); + IFileSystem fileSystem = new FileSystem(); + Mock> loggerValidator = new(); + RuntimeConfigValidator runtimeConfigValidator = new(runtimeConfigProvider, fileSystem, loggerValidator.Object); + switch (DatabaseEngine) { case TestCategory.POSTGRESQL: @@ -298,7 +304,7 @@ protected static void SetUpSQLMetadataProvider(RuntimeConfigProvider runtimeConf _sqlMetadataProvider = new PostgreSqlMetadataProvider( runtimeConfigProvider, - runtimeConfigValidator.Object, + runtimeConfigValidator, _queryManagerFactory.Object, _sqlMetadataLogger, dataSourceName); @@ -319,7 +325,7 @@ protected static void SetUpSQLMetadataProvider(RuntimeConfigProvider runtimeConf _sqlMetadataProvider = new MsSqlMetadataProvider( runtimeConfigProvider, - runtimeConfigValidator.Object, + runtimeConfigValidator, _queryManagerFactory.Object, _sqlMetadataLogger, dataSourceName); @@ -340,7 +346,7 @@ protected static void SetUpSQLMetadataProvider(RuntimeConfigProvider runtimeConf _sqlMetadataProvider = new MySqlMetadataProvider( runtimeConfigProvider, - runtimeConfigValidator.Object, + runtimeConfigValidator, _queryManagerFactory.Object, _sqlMetadataLogger, dataSourceName); @@ -361,7 +367,7 @@ protected static void SetUpSQLMetadataProvider(RuntimeConfigProvider runtimeConf _sqlMetadataProvider = new MsSqlMetadataProvider( runtimeConfigProvider, - runtimeConfigValidator.Object, + runtimeConfigValidator, _queryManagerFactory.Object, _sqlMetadataLogger, dataSourceName); diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index a8d333ad27..6d7cad4d40 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -3,11 +3,14 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.Data.Common; using System.IO; +using System.IO.Abstractions; using System.Net; using System.Text.Json.Nodes; using System.Threading.Tasks; +using Azure.DataApiBuilder.Config; using Azure.DataApiBuilder.Config.DatabasePrimitives; using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Core.Authorization; @@ -129,11 +132,13 @@ public void CheckTablePrefix(string schemaName, string tableName, string expecte queryManagerFactory.Setup(x => x.GetQueryBuilder(It.IsAny())).Returns(queryBuilder); queryManagerFactory.Setup(x => x.GetQueryExecutor(It.IsAny())).Returns(queryExecutor.Object); - Mock runtimeConfigValidator = new(); + IFileSystem fileSystem = new FileSystem(); + Mock> loggerValidator = new(); + RuntimeConfigValidator runtimeConfigValidator = new(runtimeConfigProvider, fileSystem, loggerValidator.Object); SqlMetadataProvider provider = new MsSqlMetadataProvider( runtimeConfigProvider, - runtimeConfigValidator.Object, + runtimeConfigValidator, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName); @@ -218,13 +223,15 @@ private static async Task CheckExceptionForBadConnectionStringHelperAsync(string queryManagerFactory.Setup(x => x.GetQueryBuilder(It.IsAny())).Returns(_queryBuilder); queryManagerFactory.Setup(x => x.GetQueryExecutor(It.IsAny())).Returns(_queryExecutor); - Mock runtimeConfigValidator = new(); + IFileSystem fileSystem = new FileSystem(); + Mock> loggerValidator = new(); + RuntimeConfigValidator runtimeConfigValidator = new(runtimeConfigProvider, fileSystem, loggerValidator.Object); ISqlMetadataProvider sqlMetadataProvider = databaseType switch { - TestCategory.MSSQL => new MsSqlMetadataProvider(runtimeConfigProvider, runtimeConfigValidator.Object, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), - TestCategory.MYSQL => new MySqlMetadataProvider(runtimeConfigProvider, runtimeConfigValidator.Object, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), - TestCategory.POSTGRESQL => new PostgreSqlMetadataProvider(runtimeConfigProvider, runtimeConfigValidator.Object, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), + TestCategory.MSSQL => new MsSqlMetadataProvider(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), + TestCategory.MYSQL => new MySqlMetadataProvider(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), + TestCategory.POSTGRESQL => new PostgreSqlMetadataProvider(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName), _ => throw new ArgumentException($"Invalid database type: {databaseType}") }; From f8435ae6908107fdd2ba58268603b29d5ffa65e9 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 26 Feb 2026 16:48:00 -0800 Subject: [PATCH 21/23] Fix test syntax --- src/Service.Tests/Configuration/ConfigurationTests.cs | 1 - src/Service.Tests/CosmosTests/TestBase.cs | 1 - src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs | 1 - .../GraphQLBuilder/MultipleMutationBuilderTests.cs | 1 - src/Service.Tests/SqlTests/SqlTestBase.cs | 2 -- src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs | 2 -- 6 files changed, 8 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 90b1e9e5ee..6b93b23d36 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; -using System.Configuration; using System.IdentityModel.Tokens.Jwt; using System.IO; using System.IO.Abstractions; diff --git a/src/Service.Tests/CosmosTests/TestBase.cs b/src/Service.Tests/CosmosTests/TestBase.cs index 5fe86ff3b1..dfac601023 100644 --- a/src/Service.Tests/CosmosTests/TestBase.cs +++ b/src/Service.Tests/CosmosTests/TestBase.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Configuration; using System.IO.Abstractions; using System.IO.Abstractions.TestingHelpers; using System.Linq; diff --git a/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs index 5efb8f7f61..67bfa8d2fe 100644 --- a/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/MultiSourceBuilderTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.Collections.Generic; -using System.Configuration; using System.IO; using System.IO.Abstractions; using System.IO.Abstractions.TestingHelpers; diff --git a/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs index 57ebcf8b90..ffe636f6db 100644 --- a/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/MultipleMutationBuilderTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.Collections.Generic; -using System.Configuration; using System.IO.Abstractions; using System.Linq; using System.Threading.Tasks; diff --git a/src/Service.Tests/SqlTests/SqlTestBase.cs b/src/Service.Tests/SqlTests/SqlTestBase.cs index 9b4b38cedf..4e0fa5249c 100644 --- a/src/Service.Tests/SqlTests/SqlTestBase.cs +++ b/src/Service.Tests/SqlTests/SqlTestBase.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Configuration; using System.IO; using System.IO.Abstractions; using System.Net; @@ -15,7 +14,6 @@ using System.Text.Json.Nodes; using System.Threading.Tasks; using Azure.DataApiBuilder.Auth; -using Azure.DataApiBuilder.Config; using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Core.Authorization; using Azure.DataApiBuilder.Core.Configurations; diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index 6d7cad4d40..3d858ff57c 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -3,14 +3,12 @@ using System; using System.Collections.Generic; -using System.Configuration; using System.Data.Common; using System.IO; using System.IO.Abstractions; using System.Net; using System.Text.Json.Nodes; using System.Threading.Tasks; -using Azure.DataApiBuilder.Config; using Azure.DataApiBuilder.Config.DatabasePrimitives; using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Core.Authorization; From f27293e511f14a309b6a4a8852a4600e71ab29e3 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 26 Feb 2026 17:16:03 -0800 Subject: [PATCH 22/23] Fix syntax --- src/Service.Tests/Configuration/ConfigurationTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 6b93b23d36..c21a35a3d4 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -3735,7 +3735,6 @@ type Planet @model(name:""PlanetAlias"") { }); FileSystemRuntimeConfigLoader loader = new(fileSystem); RuntimeConfigProvider provider = new(loader); - Mock> loggerValidator = new(); RuntimeConfigValidator validator = new(provider, fileSystem, loggerValidator.Object); From 77f080355f8ee9c81394a218d0b97ecf20eed292 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 26 Feb 2026 21:27:57 -0800 Subject: [PATCH 23/23] Fix tests --- .../Caching/DabCacheServiceIntegrationTests.cs | 5 +++++ src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs b/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs index 68c9225b96..91c6ef28bd 100644 --- a/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs +++ b/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Data.Common; +using System.IO.Abstractions; using System.Net; using System.Reflection; using System.Text.Json; @@ -699,10 +700,14 @@ private static Mock CreateMockSqlQueryStructure(string entity entityToDatabaseObject.Add(entityName, new DatabaseTable()); Mock mockRuntimeConfigProvider = CreateMockRuntimeConfigProvider(entityName); + IFileSystem fileSystem = new FileSystem(); + Mock> loggerValidator = new(); + RuntimeConfigValidator runtimeConfigValidator = new(mockRuntimeConfigProvider.Object, fileSystem, loggerValidator.Object); Mock mockQueryFactory = new(); Mock> mockLogger = new(); Mock mockSqlMetadataProvider = new( mockRuntimeConfigProvider.Object, + runtimeConfigValidator, mockQueryFactory.Object, mockLogger.Object, dataSourceName, diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index 3d858ff57c..866e232afe 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -490,11 +490,13 @@ public async Task ValidateExceptionForInvalidResultFieldNames(string invalidFiel queryManagerFactory.Setup(x => x.GetQueryBuilder(It.IsAny())).Returns(_queryBuilder); queryManagerFactory.Setup(x => x.GetQueryExecutor(It.IsAny())).Returns(mockQueryExecutor.Object); - Mock runtimeConfigValidator = new(); + IFileSystem fileSystem = new FileSystem(); + Mock> loggerValidator = new(); + RuntimeConfigValidator runtimeConfigValidator = new(runtimeConfigProvider, fileSystem, loggerValidator.Object); ISqlMetadataProvider sqlMetadataProvider = new MsSqlMetadataProvider( runtimeConfigProvider, - runtimeConfigValidator.Object, + runtimeConfigValidator, queryManagerFactory.Object, sqlMetadataLogger, dataSourceName);