Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
edcade8
Changes for autoentities
RubenCerna2079 Jan 22, 2026
c380bb6
Add query and its execution
RubenCerna2079 Jan 26, 2026
aacecaa
Add testing
RubenCerna2079 Jan 27, 2026
d8b9856
Changes based on comments
RubenCerna2079 Jan 29, 2026
a8dfa5b
Changes based on comments
RubenCerna2079 Jan 29, 2026
35d335f
Added changes based on comments
RubenCerna2079 Jan 30, 2026
5cf3424
Fix formatting issues
RubenCerna2079 Jan 30, 2026
4dd7c0e
Generate in-memory entities
RubenCerna2079 Feb 5, 2026
687c6c8
Changes to generate autoentities as entities
RubenCerna2079 Feb 11, 2026
f44e5ab
Add new testing
RubenCerna2079 Feb 12, 2026
ce3bb09
Fix grammar errors
RubenCerna2079 Feb 13, 2026
bcd0a52
Changes to ensure autoentities work with multiple data sources
RubenCerna2079 Feb 20, 2026
b0b426e
Move validation of entities
RubenCerna2079 Feb 21, 2026
923a7af
Change test
RubenCerna2079 Feb 21, 2026
e521b08
Add testing
RubenCerna2079 Feb 21, 2026
bbf5ceb
Refactor code
RubenCerna2079 Feb 23, 2026
9e62a65
Fix Tests
RubenCerna2079 Feb 25, 2026
18c90d8
Fix syntax errors
RubenCerna2079 Feb 26, 2026
ef3c23e
Changes based on comments
RubenCerna2079 Feb 26, 2026
9d30fd9
Fix test errors by replacing mock with real object
RubenCerna2079 Feb 27, 2026
f8435ae
Fix test syntax
RubenCerna2079 Feb 27, 2026
f27293e
Fix syntax
RubenCerna2079 Feb 27, 2026
77f0803
Fix tests
RubenCerna2079 Feb 27, 2026
b50069e
Changes based on comment
RubenCerna2079 Feb 28, 2026
974b455
Merge branch 'main' into dev/rubencerna/update-validation-autoentities
RubenCerna2079 Feb 28, 2026
01af5d0
Merge branch 'main' into dev/rubencerna/update-validation-autoentities
RubenCerna2079 Mar 3, 2026
c34e9e1
Change ValidateEntitiesMetadata
RubenCerna2079 Mar 4, 2026
c8a8e5c
Fix unit tests
RubenCerna2079 Mar 4, 2026
6de44f8
Fix tests
RubenCerna2079 Mar 4, 2026
3ad3849
Revert addition of metadata provider factory
RubenCerna2079 Mar 5, 2026
6cb0e80
Revert addition of metadata provider factory
RubenCerna2079 Mar 5, 2026
a50bb36
Revert use of metadata provider factory
RubenCerna2079 Mar 5, 2026
6ec2169
Changes based on comments
RubenCerna2079 Mar 5, 2026
6fd890d
Merge branch 'main' into dev/rubencerna/update-validation-autoentities
RubenCerna2079 Mar 5, 2026
0745070
Add validation test
RubenCerna2079 Mar 5, 2026
6aa5794
Use validation in CosmosDB
RubenCerna2079 Mar 5, 2026
80cdb94
Added comments to explain what the validation does
RubenCerna2079 Mar 5, 2026
ce819e5
Add missing test summary
RubenCerna2079 Mar 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions src/Core/Configurations/RuntimeConfigValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}

/// <summary>
Expand Down Expand Up @@ -334,6 +322,11 @@ public async Task<bool> TryValidateConfig(

_logger.LogInformation("Validating entity relationships.");
ValidateRelationshipConfigCorrectness(runtimeConfig);

// This function initializes the metadata providers which in turn validates the connectivity to the
// database and also validates all the REST and GraphQL paths as well as the permissions of the entities
// that are created from the 'Entities' and 'Autoentities' configuration, including the relationships defined in the config against the database metadata.
// Any exceptions caught during this process are added to the ConfigValidationExceptions list and logged at the end of this function.
await ValidateEntitiesMetadata(runtimeConfig, loggerFactory);

if (validationResult.IsValid && !ConfigValidationExceptions.Any())
Expand Down Expand Up @@ -486,6 +479,8 @@ public void ValidateRelationshipConfigCorrectness(RuntimeConfig runtimeConfig)
/// This method validates the entities relationships against the database objects using
/// metadata from the backend DB generated by this function.
/// </summary>
/// NOTE: This function should not be used in the regular flow of DAB as we already initialize the metadata providers during startup,
/// doing it again will cause the application to fail as it will try to add data that is already present.
public async Task ValidateEntitiesMetadata(RuntimeConfig runtimeConfig, ILoggerFactory loggerFactory)
{
// Only used for validation so we don't need the handler which is for hot reload scenarios.
Expand All @@ -499,6 +494,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<ISqlMetadataProvider>(),
fileSystem: _fileSystem,
Expand Down Expand Up @@ -1594,4 +1590,26 @@ private static bool IsLoggerFilterValid(string loggerFilter)

return false;
}

/// <summary>
/// Checks that all of the entities created with the Entities and Autoentities properties
/// 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.
/// </summary>
/// <param name="runtimeConfig">The runtime configuration.</param>
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class CosmosSqlMetadataProvider : ISqlMetadataProvider

public List<Exception> 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;
Expand All @@ -76,6 +76,7 @@ public CosmosSqlMetadataProvider(RuntimeConfigProvider runtimeConfigProvider, IF
subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization);
}

runtimeConfigValidator.ValidateEntityAndAutoentityConfigurations(runtimeConfig);
_cosmosDb = cosmosDb;
ParseSchemaGraphQLDocument();

Expand Down
13 changes: 8 additions & 5 deletions src/Core/Services/MetadataProviders/MetadataProviderFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ public class MetadataProviderFactory : IMetadataProviderFactory
{
private readonly IDictionary<string, ISqlMetadataProvider> _metadataProviders;
private readonly RuntimeConfigProvider _runtimeConfigProvider;
private readonly RuntimeConfigValidator _runtimeConfigValidator;
private readonly IAbstractQueryManagerFactory _queryManagerFactory;
private readonly ILogger<ISqlMetadataProvider> _logger;
private readonly IFileSystem _fileSystem;
private readonly bool _isValidateOnly;

public MetadataProviderFactory(
RuntimeConfigProvider runtimeConfigProvider,
RuntimeConfigValidator runtimeConfigValidator,
IAbstractQueryManagerFactory queryManagerFactory,
ILogger<ISqlMetadataProvider> logger,
IFileSystem fileSystem,
Expand All @@ -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;
Expand All @@ -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),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ public class MsSqlMetadataProvider :

public MsSqlMetadataProvider(
RuntimeConfigProvider runtimeConfigProvider,
RuntimeConfigValidator runtimeConfigValidator,
IAbstractQueryManagerFactory queryManagerFactory,
ILogger<ISqlMetadataProvider> logger,
string dataSourceName,
bool isValidateOnly = false)
: base(runtimeConfigProvider, queryManagerFactory, logger, dataSourceName, isValidateOnly)
: base(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory, logger, dataSourceName, isValidateOnly)
{
_runtimeConfigProvider = runtimeConfigProvider;
}
Expand Down Expand Up @@ -374,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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ public class MySqlMetadataProvider : SqlMetadataProvider<MySqlConnection, MySqlD

public MySqlMetadataProvider(
RuntimeConfigProvider runtimeConfigProvider,
RuntimeConfigValidator runtimeConfigValidator,
IAbstractQueryManagerFactory queryManagerFactory,
ILogger<ISqlMetadataProvider> logger,
string dataSourceName,
bool isValidateOnly = false)
: base(runtimeConfigProvider, queryManagerFactory, logger, dataSourceName, isValidateOnly)
: base(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory, logger, dataSourceName, isValidateOnly)
{
try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ public class PostgreSqlMetadataProvider :

public PostgreSqlMetadataProvider(
RuntimeConfigProvider runtimeConfigProvider,
RuntimeConfigValidator runtimeConfigValidator,
IAbstractQueryManagerFactory queryManagerFactory,
ILogger<ISqlMetadataProvider> logger,
string dataSourceName,
bool isValidateOnly = false)
: base(runtimeConfigProvider, queryManagerFactory, logger, dataSourceName, isValidateOnly)
: base(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory, logger, dataSourceName, isValidateOnly)
{
}

Expand Down
21 changes: 16 additions & 5 deletions src/Core/Services/MetadataProviders/SqlMetadataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ public abstract class SqlMetadataProvider<ConnectionT, DataAdapterT, CommandT> :

private RuntimeConfigProvider _runtimeConfigProvider;

private RuntimeConfigValidator _runtimeConfigValidator;

private Dictionary<string, Dictionary<string, string>> EntityBackingColumnsToExposedNames { get; } = new();

private Dictionary<string, Dictionary<string, string>> EntityExposedNamesToBackingColumnNames { get; } = new();
Expand Down Expand Up @@ -108,13 +110,15 @@ private void HandleOrRecordException(Exception e)

public SqlMetadataProvider(
RuntimeConfigProvider runtimeConfigProvider,
RuntimeConfigValidator runtimeConfigValidator,
IAbstractQueryManagerFactory engineFactory,
ILogger<ISqlMetadataProvider> logger,
string dataSourceName,
bool isValidateOnly = false)
{
RuntimeConfig runtimeConfig = runtimeConfigProvider.GetConfig();
_runtimeConfigProvider = runtimeConfigProvider;
_runtimeConfigValidator = runtimeConfigValidator;
_dataSourceName = dataSourceName;
_databaseType = runtimeConfig.GetDataSourceFromDataSourceName(dataSourceName).DatabaseType;
_logger = logger;
Expand Down Expand Up @@ -310,12 +314,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);
}

GenerateDatabaseObjectForEntities();
if (_isValidateOnly)
{
// Currently Validate mode only support single datasource,
Expand All @@ -332,8 +331,20 @@ public async Task InitializeAsync()
}
}

if (GetDatabaseType() == DatabaseType.MSSQL)
{
await GenerateAutoentitiesIntoEntities(Autoentities);
}

// Running these entity 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.
if (!_runtimeConfigProvider.IsLateConfigured)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -699,10 +700,14 @@ private static Mock<SqlQueryStructure> CreateMockSqlQueryStructure(string entity
entityToDatabaseObject.Add(entityName, new DatabaseTable());

Mock<RuntimeConfigProvider> mockRuntimeConfigProvider = CreateMockRuntimeConfigProvider(entityName);
IFileSystem fileSystem = new FileSystem();
Mock<ILogger<RuntimeConfigValidator>> loggerValidator = new();
RuntimeConfigValidator runtimeConfigValidator = new(mockRuntimeConfigProvider.Object, fileSystem, loggerValidator.Object);
Mock<IAbstractQueryManagerFactory> mockQueryFactory = new();
Mock<ILogger<ISqlMetadataProvider>> mockLogger = new();
Mock<MsSqlMetadataProvider> mockSqlMetadataProvider = new(
mockRuntimeConfigProvider.Object,
runtimeConfigValidator,
mockQueryFactory.Object,
mockLogger.Object,
dataSourceName,
Expand Down
Loading