Skip to content

Commit b2310d0

Browse files
Update Validation for Autoentities (#3152)
## Why make this change? - #3131 The current validation for DAB occurs before the section where the autoentities are generated. In order to ensure that all the entities that are generated from the autoentities, we need to move the part of the validation that takes care of ensuring the entities don't have the same REST paths or the same GraphQL types. ## What is this change? ## How was this tested? - [ ] Integration Tests - [X] Unit Tests
1 parent 77b44a0 commit b2310d0

15 files changed

Lines changed: 318 additions & 43 deletions

src/Core/Configurations/RuntimeConfigValidator.cs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,6 @@ public void ValidateConfigProperties()
9696
ValidateLoggerFilters(runtimeConfig);
9797
ValidateAzureLogAnalyticsAuth(runtimeConfig);
9898
ValidateFileSinkPath(runtimeConfig);
99-
100-
// Running these graphQL validations only in development mode to ensure
101-
// fast startup of engine in production mode.
102-
if (runtimeConfig.IsDevelopmentMode())
103-
{
104-
ValidateEntityConfiguration(runtimeConfig);
105-
106-
if (runtimeConfig.IsGraphQLEnabled)
107-
{
108-
ValidateEntitiesDoNotGenerateDuplicateQueriesOrMutation(runtimeConfig.DataSource.DatabaseType, runtimeConfig.Entities);
109-
}
110-
}
11199
}
112100

113101
/// <summary>
@@ -334,6 +322,11 @@ public async Task<bool> TryValidateConfig(
334322

335323
_logger.LogInformation("Validating entity relationships.");
336324
ValidateRelationshipConfigCorrectness(runtimeConfig);
325+
326+
// This function initializes the metadata providers which in turn validates the connectivity to the
327+
// database and also validates all the REST and GraphQL paths as well as the permissions of the entities
328+
// that are created from the 'Entities' and 'Autoentities' configuration, including the relationships defined in the config against the database metadata.
329+
// Any exceptions caught during this process are added to the ConfigValidationExceptions list and logged at the end of this function.
337330
await ValidateEntitiesMetadata(runtimeConfig, loggerFactory);
338331

339332
if (validationResult.IsValid && !ConfigValidationExceptions.Any())
@@ -486,6 +479,8 @@ public void ValidateRelationshipConfigCorrectness(RuntimeConfig runtimeConfig)
486479
/// This method validates the entities relationships against the database objects using
487480
/// metadata from the backend DB generated by this function.
488481
/// </summary>
482+
/// NOTE: This function should not be used in the regular flow of DAB as we already initialize the metadata providers during startup,
483+
/// doing it again will cause the application to fail as it will try to add data that is already present.
489484
public async Task ValidateEntitiesMetadata(RuntimeConfig runtimeConfig, ILoggerFactory loggerFactory)
490485
{
491486
// Only used for validation so we don't need the handler which is for hot reload scenarios.
@@ -499,6 +494,7 @@ public async Task ValidateEntitiesMetadata(RuntimeConfig runtimeConfig, ILoggerF
499494
// Only used for validation so we don't need the handler which is for hot reload scenarios.
500495
MetadataProviderFactory metadataProviderFactory = new(
501496
runtimeConfigProvider: _runtimeConfigProvider,
497+
runtimeConfigValidator: this,
502498
queryManagerFactory: queryManagerFactory,
503499
logger: loggerFactory.CreateLogger<ISqlMetadataProvider>(),
504500
fileSystem: _fileSystem,
@@ -1594,4 +1590,26 @@ private static bool IsLoggerFilterValid(string loggerFilter)
15941590

15951591
return false;
15961592
}
1593+
1594+
/// <summary>
1595+
/// Checks that all of the entities created with the Entities and Autoentities properties
1596+
/// are valid by having unique paths for both REST and GraphQL, that there are no duplicate
1597+
/// Queries or Mutation entities, and ensure the semantic correctness of all the entities.
1598+
/// </summary>
1599+
/// <param name="runtimeConfig">The runtime configuration.</param>
1600+
public void ValidateEntityAndAutoentityConfigurations(RuntimeConfig runtimeConfig)
1601+
{
1602+
if (runtimeConfig.IsDevelopmentMode())
1603+
{
1604+
ValidateEntityConfiguration(runtimeConfig);
1605+
1606+
if (runtimeConfig.IsGraphQLEnabled)
1607+
{
1608+
ValidateEntitiesDoNotGenerateDuplicateQueriesOrMutation(runtimeConfig.DataSource.DatabaseType, runtimeConfig.Entities);
1609+
}
1610+
1611+
// Running only in developer mode to ensure fast and smooth startup in production.
1612+
ValidatePermissionsInConfig(runtimeConfig);
1613+
}
1614+
}
15971615
}

src/Core/Services/MetadataProviders/CosmosSqlMetadataProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public class CosmosSqlMetadataProvider : ISqlMetadataProvider
5555

5656
public List<Exception> SqlMetadataExceptions { get; private set; } = new();
5757

58-
public CosmosSqlMetadataProvider(RuntimeConfigProvider runtimeConfigProvider, IFileSystem fileSystem)
58+
public CosmosSqlMetadataProvider(RuntimeConfigProvider runtimeConfigProvider, RuntimeConfigValidator runtimeConfigValidator, IFileSystem fileSystem)
5959
{
6060
RuntimeConfig runtimeConfig = runtimeConfigProvider.GetConfig();
6161
_fileSystem = fileSystem;
@@ -76,6 +76,7 @@ public CosmosSqlMetadataProvider(RuntimeConfigProvider runtimeConfigProvider, IF
7676
subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization);
7777
}
7878

79+
runtimeConfigValidator.ValidateEntityAndAutoentityConfigurations(runtimeConfig);
7980
_cosmosDb = cosmosDb;
8081
ParseSchemaGraphQLDocument();
8182

src/Core/Services/MetadataProviders/MetadataProviderFactory.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ public class MetadataProviderFactory : IMetadataProviderFactory
1919
{
2020
private readonly IDictionary<string, ISqlMetadataProvider> _metadataProviders;
2121
private readonly RuntimeConfigProvider _runtimeConfigProvider;
22+
private readonly RuntimeConfigValidator _runtimeConfigValidator;
2223
private readonly IAbstractQueryManagerFactory _queryManagerFactory;
2324
private readonly ILogger<ISqlMetadataProvider> _logger;
2425
private readonly IFileSystem _fileSystem;
2526
private readonly bool _isValidateOnly;
2627

2728
public MetadataProviderFactory(
2829
RuntimeConfigProvider runtimeConfigProvider,
30+
RuntimeConfigValidator runtimeConfigValidator,
2931
IAbstractQueryManagerFactory queryManagerFactory,
3032
ILogger<ISqlMetadataProvider> logger,
3133
IFileSystem fileSystem,
@@ -34,6 +36,7 @@ public MetadataProviderFactory(
3436
{
3537
handler?.Subscribe(METADATA_PROVIDER_FACTORY_ON_CONFIG_CHANGED, OnConfigChanged);
3638
_runtimeConfigProvider = runtimeConfigProvider;
39+
_runtimeConfigValidator = runtimeConfigValidator;
3740
_queryManagerFactory = queryManagerFactory;
3841
_logger = logger;
3942
_fileSystem = fileSystem;
@@ -48,11 +51,11 @@ private void ConfigureMetadataProviders()
4851
{
4952
ISqlMetadataProvider metadataProvider = dataSource.DatabaseType switch
5053
{
51-
DatabaseType.CosmosDB_NoSQL => new CosmosSqlMetadataProvider(_runtimeConfigProvider, _fileSystem),
52-
DatabaseType.MSSQL => new MsSqlMetadataProvider(_runtimeConfigProvider, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly),
53-
DatabaseType.DWSQL => new MsSqlMetadataProvider(_runtimeConfigProvider, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly),
54-
DatabaseType.PostgreSQL => new PostgreSqlMetadataProvider(_runtimeConfigProvider, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly),
55-
DatabaseType.MySQL => new MySqlMetadataProvider(_runtimeConfigProvider, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly),
54+
DatabaseType.CosmosDB_NoSQL => new CosmosSqlMetadataProvider(_runtimeConfigProvider, _runtimeConfigValidator, _fileSystem),
55+
DatabaseType.MSSQL => new MsSqlMetadataProvider(_runtimeConfigProvider, _runtimeConfigValidator, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly),
56+
DatabaseType.DWSQL => new MsSqlMetadataProvider(_runtimeConfigProvider, _runtimeConfigValidator, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly),
57+
DatabaseType.PostgreSQL => new PostgreSqlMetadataProvider(_runtimeConfigProvider, _runtimeConfigValidator, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly),
58+
DatabaseType.MySQL => new MySqlMetadataProvider(_runtimeConfigProvider, _runtimeConfigValidator, _queryManagerFactory, _logger, dataSourceName, _isValidateOnly),
5659
_ => throw new NotSupportedException(dataSource.DatabaseTypeNotSupportedMessage),
5760
};
5861

src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ public class MsSqlMetadataProvider :
3333

3434
public MsSqlMetadataProvider(
3535
RuntimeConfigProvider runtimeConfigProvider,
36+
RuntimeConfigValidator runtimeConfigValidator,
3637
IAbstractQueryManagerFactory queryManagerFactory,
3738
ILogger<ISqlMetadataProvider> logger,
3839
string dataSourceName,
3940
bool isValidateOnly = false)
40-
: base(runtimeConfigProvider, queryManagerFactory, logger, dataSourceName, isValidateOnly)
41+
: base(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory, logger, dataSourceName, isValidateOnly)
4142
{
4243
_runtimeConfigProvider = runtimeConfigProvider;
4344
}
@@ -374,7 +375,7 @@ protected override async Task GenerateAutoentitiesIntoEntities(IReadOnlyDictiona
374375

375376
if (addedEntities == 0)
376377
{
377-
_logger.LogWarning($"No new entities were generated from the autoentity {autoentityName} defined in the configuration.");
378+
_logger.LogWarning("No new entities were generated from the autoentity {autoentityName} defined in the configuration.", autoentityName);
378379
}
379380
}
380381

src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ public class MySqlMetadataProvider : SqlMetadataProvider<MySqlConnection, MySqlD
2323

2424
public MySqlMetadataProvider(
2525
RuntimeConfigProvider runtimeConfigProvider,
26+
RuntimeConfigValidator runtimeConfigValidator,
2627
IAbstractQueryManagerFactory queryManagerFactory,
2728
ILogger<ISqlMetadataProvider> logger,
2829
string dataSourceName,
2930
bool isValidateOnly = false)
30-
: base(runtimeConfigProvider, queryManagerFactory, logger, dataSourceName, isValidateOnly)
31+
: base(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory, logger, dataSourceName, isValidateOnly)
3132
{
3233
try
3334
{

src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ public class PostgreSqlMetadataProvider :
2222

2323
public PostgreSqlMetadataProvider(
2424
RuntimeConfigProvider runtimeConfigProvider,
25+
RuntimeConfigValidator runtimeConfigValidator,
2526
IAbstractQueryManagerFactory queryManagerFactory,
2627
ILogger<ISqlMetadataProvider> logger,
2728
string dataSourceName,
2829
bool isValidateOnly = false)
29-
: base(runtimeConfigProvider, queryManagerFactory, logger, dataSourceName, isValidateOnly)
30+
: base(runtimeConfigProvider, runtimeConfigValidator, queryManagerFactory, logger, dataSourceName, isValidateOnly)
3031
{
3132
}
3233

src/Core/Services/MetadataProviders/SqlMetadataProvider.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ public abstract class SqlMetadataProvider<ConnectionT, DataAdapterT, CommandT> :
7777

7878
private RuntimeConfigProvider _runtimeConfigProvider;
7979

80+
private RuntimeConfigValidator _runtimeConfigValidator;
81+
8082
private Dictionary<string, Dictionary<string, string>> EntityBackingColumnsToExposedNames { get; } = new();
8183

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

109111
public SqlMetadataProvider(
110112
RuntimeConfigProvider runtimeConfigProvider,
113+
RuntimeConfigValidator runtimeConfigValidator,
111114
IAbstractQueryManagerFactory engineFactory,
112115
ILogger<ISqlMetadataProvider> logger,
113116
string dataSourceName,
114117
bool isValidateOnly = false)
115118
{
116119
RuntimeConfig runtimeConfig = runtimeConfigProvider.GetConfig();
117120
_runtimeConfigProvider = runtimeConfigProvider;
121+
_runtimeConfigValidator = runtimeConfigValidator;
118122
_dataSourceName = dataSourceName;
119123
_databaseType = runtimeConfig.GetDataSourceFromDataSourceName(dataSourceName).DatabaseType;
120124
_logger = logger;
@@ -310,12 +314,7 @@ public string GetEntityName(string graphQLType)
310314
public async Task InitializeAsync()
311315
{
312316
System.Diagnostics.Stopwatch timer = System.Diagnostics.Stopwatch.StartNew();
313-
if (GetDatabaseType() == DatabaseType.MSSQL)
314-
{
315-
await GenerateAutoentitiesIntoEntities(Autoentities);
316-
}
317317

318-
GenerateDatabaseObjectForEntities();
319318
if (_isValidateOnly)
320319
{
321320
// Currently Validate mode only support single datasource,
@@ -332,8 +331,20 @@ public async Task InitializeAsync()
332331
}
333332
}
334333

334+
if (GetDatabaseType() == DatabaseType.MSSQL)
335+
{
336+
await GenerateAutoentitiesIntoEntities(Autoentities);
337+
}
338+
339+
// Running these entity validations only in development mode to ensure
340+
// fast startup of engine in production mode.
341+
RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig();
342+
_runtimeConfigValidator.ValidateEntityAndAutoentityConfigurations(runtimeConfig);
343+
344+
GenerateDatabaseObjectForEntities();
335345
await PopulateObjectDefinitionForEntities();
336346
GenerateExposedToBackingColumnMapsForEntities();
347+
337348
// When IsLateConfigured is true we are in a hosted scenario and do not reveal primary key information.
338349
if (!_runtimeConfigProvider.IsLateConfigured)
339350
{

src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Data.Common;
8+
using System.IO.Abstractions;
89
using System.Net;
910
using System.Reflection;
1011
using System.Text.Json;
@@ -699,10 +700,14 @@ private static Mock<SqlQueryStructure> CreateMockSqlQueryStructure(string entity
699700
entityToDatabaseObject.Add(entityName, new DatabaseTable());
700701

701702
Mock<RuntimeConfigProvider> mockRuntimeConfigProvider = CreateMockRuntimeConfigProvider(entityName);
703+
IFileSystem fileSystem = new FileSystem();
704+
Mock<ILogger<RuntimeConfigValidator>> loggerValidator = new();
705+
RuntimeConfigValidator runtimeConfigValidator = new(mockRuntimeConfigProvider.Object, fileSystem, loggerValidator.Object);
702706
Mock<IAbstractQueryManagerFactory> mockQueryFactory = new();
703707
Mock<ILogger<ISqlMetadataProvider>> mockLogger = new();
704708
Mock<MsSqlMetadataProvider> mockSqlMetadataProvider = new(
705709
mockRuntimeConfigProvider.Object,
710+
runtimeConfigValidator,
706711
mockQueryFactory.Object,
707712
mockLogger.Object,
708713
dataSourceName,

0 commit comments

Comments
 (0)