From 0a7e82746113641977b8a79a3157da2ba10567aa Mon Sep 17 00:00:00 2001 From: Rohan Khanna Date: Mon, 18 Mar 2024 13:00:57 -0700 Subject: [PATCH 1/7] Adding datasourceName during executeQueryAsync --- src/Core/Services/MetadataProviders/SqlMetadataProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 900812c9bb..8817821e68 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -1183,7 +1183,8 @@ private async Task PopulateColumnDefinitionsWithReadOnlyFlag(string tableName, s List? readOnlyFields = await QueryExecutor.ExecuteQueryAsync( sqltext: queryToGetReadOnlyColumns, parameters: parameters, - dataReaderHandler: SummarizeReadOnlyFieldsMetadata); + dataReaderHandler: SummarizeReadOnlyFieldsMetadata, + dataSourceName: _dataSourceName); if (readOnlyFields is not null && readOnlyFields.Count > 0) { From dbd55ba76df79398af982418a1e6f2d38520dfa3 Mon Sep 17 00:00:00 2001 From: Rohan Khanna Date: Mon, 18 Mar 2024 13:09:01 -0700 Subject: [PATCH 2/7] Adding explicit datasourceName for populateTriggerMetadataForTable. --- src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index c9cda7e8fd..3bc4e876e1 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -66,7 +66,8 @@ public override async Task PopulateTriggerMetadataForTable(string entityName, st JsonArray? resultArray = await QueryExecutor.ExecuteQueryAsync( sqltext: enumerateEnabledTriggers, parameters: parameters, - dataReaderHandler: QueryExecutor.GetJsonArrayAsync); + dataReaderHandler: QueryExecutor.GetJsonArrayAsync, + dataSourceName: _dataSourceName); using JsonDocument sqlResult = JsonDocument.Parse(resultArray!.ToJsonString()); foreach (JsonElement element in sqlResult.RootElement.EnumerateArray()) From 9117a11b194140b44327a03d2c379f3eba637d31 Mon Sep 17 00:00:00 2001 From: Rohan Khanna Date: Mon, 18 Mar 2024 13:40:02 -0700 Subject: [PATCH 3/7] Adding tests in which datasourceName is explicitly specified. --- src/Core/Resolvers/IQueryExecutor.cs | 4 ++-- src/Core/Resolvers/QueryExecutor.cs | 4 ++-- src/Core/Resolvers/SqlMutationEngine.cs | 9 +++++---- .../Services/MetadataProviders/SqlMetadataProvider.cs | 2 +- src/Service.Tests/SqlTests/SqlTestBase.cs | 9 ++++++--- .../Unittests/MultiSourceQueryExecutionUnitTests.cs | 6 ++++++ .../Unittests/SqlQueryExecutorUnitTests.cs | 10 ++++++---- 7 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/Core/Resolvers/IQueryExecutor.cs b/src/Core/Resolvers/IQueryExecutor.cs index a9bb00b2a0..f16a5a4e82 100644 --- a/src/Core/Resolvers/IQueryExecutor.cs +++ b/src/Core/Resolvers/IQueryExecutor.cs @@ -30,9 +30,9 @@ public interface IQueryExecutor string sqltext, IDictionary parameters, Func?, Task>? dataReaderHandler, + string dataSourceName, HttpContext? httpContext = null, - List? args = null, - string dataSourceName = ""); + List? args = null); /// /// Extracts the rows from the given DbDataReader to populate diff --git a/src/Core/Resolvers/QueryExecutor.cs b/src/Core/Resolvers/QueryExecutor.cs index 6695736ee2..24675c36ec 100644 --- a/src/Core/Resolvers/QueryExecutor.cs +++ b/src/Core/Resolvers/QueryExecutor.cs @@ -65,9 +65,9 @@ public QueryExecutor(DbExceptionParser dbExceptionParser, string sqltext, IDictionary parameters, Func?, Task>? dataReaderHandler, + string dataSourceName, HttpContext? httpContext = null, - List? args = null, - string dataSourceName = "") + List? args = null) { if (string.IsNullOrEmpty(dataSourceName)) { diff --git a/src/Core/Resolvers/SqlMutationEngine.cs b/src/Core/Resolvers/SqlMutationEngine.cs index 4485b4aa83..6bad50ea52 100644 --- a/src/Core/Resolvers/SqlMutationEngine.cs +++ b/src/Core/Resolvers/SqlMutationEngine.cs @@ -277,6 +277,7 @@ await queryExecutor.ExecuteQueryAsync( queryText, executeQueryStructure.Parameters, queryExecutor.GetJsonArrayAsync, + dataSourceName, GetHttpContext()); transactionScope.Complete(); @@ -840,9 +841,9 @@ await queryExecutor.ExecuteQueryAsync( queryString, queryParameters, queryExecutor.ExtractResultSetFromDbDataReader, + dataSourceName, GetHttpContext(), - primaryKeyExposedColumnNames.Count > 0 ? primaryKeyExposedColumnNames : sourceDefinition.PrimaryKey, - dataSourceName); + primaryKeyExposedColumnNames.Count > 0 ? primaryKeyExposedColumnNames : sourceDefinition.PrimaryKey); dbResultSetRow = dbResultSet is not null ? (dbResultSet.Rows.FirstOrDefault() ?? new DbResultSetRow()) : null; @@ -991,9 +992,9 @@ private async Task queryString, queryParameters, queryExecutor.GetMultipleResultSetsIfAnyAsync, + dataSourceName, GetHttpContext(), - new List { prettyPrintPk, entityName }, - dataSourceName); + new List { prettyPrintPk, entityName }); } private Dictionary PrepareParameters(RestRequestContext context) diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 8817821e68..65027306f2 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -1497,7 +1497,7 @@ private async Task PopulateForeignKeyDefinitionAsync() // Gather all the referencing and referenced columns for each pair // of referencing and referenced tables. PairToFkDefinition = await QueryExecutor.ExecuteQueryAsync( - queryForForeignKeyInfo, parameters, SummarizeFkMetadata, httpContext: null, args: null, _dataSourceName); + queryForForeignKeyInfo, parameters, SummarizeFkMetadata, _dataSourceName, httpContext: null, args: null); if (PairToFkDefinition is not null) { diff --git a/src/Service.Tests/SqlTests/SqlTestBase.cs b/src/Service.Tests/SqlTests/SqlTestBase.cs index b3fdd6f6d9..f9348d3bd7 100644 --- a/src/Service.Tests/SqlTests/SqlTestBase.cs +++ b/src/Service.Tests/SqlTests/SqlTestBase.cs @@ -238,7 +238,7 @@ private static async Task ExecuteQueriesOnDbAsync(List customQueries) { foreach (string query in customQueries) { - await _queryExecutor.ExecuteQueryAsync(query, parameters: null, dataReaderHandler: null); + await _queryExecutor.ExecuteQueryAsync(query, dataSourceName: string.Empty, parameters: null, dataReaderHandler: null); } } } @@ -367,6 +367,7 @@ protected static async Task ResetDbStateAsync() { await _queryExecutor.ExecuteQueryAsync( File.ReadAllText($"DatabaseSchema-{DatabaseEngine}.sql"), + dataSourceName: string.Empty, parameters: null, dataReaderHandler: null); } @@ -388,7 +389,8 @@ protected static async Task GetDatabaseResultAsync( await _queryExecutor.ExecuteQueryAsync( queryText, parameters: null, - _queryExecutor.GetJsonResultAsync); + _queryExecutor.GetJsonResultAsync, + string.Empty); result = sqlResult is not null ? sqlResult.RootElement.ToString() : @@ -400,7 +402,8 @@ await _queryExecutor.ExecuteQueryAsync( await _queryExecutor.ExecuteQueryAsync( queryText, parameters: null, - _queryExecutor.GetJsonArrayAsync); + _queryExecutor.GetJsonArrayAsync, + string.Empty); using JsonDocument sqlResult = resultArray is not null ? JsonDocument.Parse(resultArray.ToJsonString()) : null; result = sqlResult is not null ? sqlResult.RootElement.ToString() : null; } diff --git a/src/Service.Tests/Unittests/MultiSourceQueryExecutionUnitTests.cs b/src/Service.Tests/Unittests/MultiSourceQueryExecutionUnitTests.cs index 59a5182eaf..d71d8ce835 100644 --- a/src/Service.Tests/Unittests/MultiSourceQueryExecutionUnitTests.cs +++ b/src/Service.Tests/Unittests/MultiSourceQueryExecutionUnitTests.cs @@ -12,6 +12,8 @@ using Azure.DataApiBuilder.Core.Models; using Azure.DataApiBuilder.Core.Resolvers; using Azure.DataApiBuilder.Core.Resolvers.Factories; +using Azure.DataApiBuilder.Core.Services; +using Azure.DataApiBuilder.Core.Services.MetadataProviders; using Azure.DataApiBuilder.Service.GraphQLBuilder.Directives; using Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; using Azure.DataApiBuilder.Service.Services; @@ -119,8 +121,12 @@ public async Task TestMultiSourceQuery() Mock mockLoader = new(null); mockLoader.Setup(x => x.TryLoadKnownConfig(out mockConfig1, It.IsAny())).Returns(true); + Mock queryManagerFactory = new(); + Mock> logger = new(); RuntimeConfigProvider provider = new(mockLoader.Object); + IMetadataProviderFactory metadataProviderFactory = new MetadataProviderFactory(provider, queryManagerFactory.Object, logger.Object, null); + // Using a sample schema file to test multi-source query. // Schema file contains some sample entities that we can test against. string graphQLSchema = File.ReadAllText("MultiSourceTestSchema.gql"); diff --git a/src/Service.Tests/Unittests/SqlQueryExecutorUnitTests.cs b/src/Service.Tests/Unittests/SqlQueryExecutorUnitTests.cs index f49f7ca78c..39b468115e 100644 --- a/src/Service.Tests/Unittests/SqlQueryExecutorUnitTests.cs +++ b/src/Service.Tests/Unittests/SqlQueryExecutorUnitTests.cs @@ -184,9 +184,9 @@ Mock queryExecutor It.IsAny(), It.IsAny>(), It.IsAny, Task>>(), + It.IsAny(), It.IsAny(), - It.IsAny>(), - It.IsAny())).CallBase(); + It.IsAny>())).CallBase(); DataApiBuilderException ex = await Assert.ThrowsExceptionAsync(async () => { @@ -194,6 +194,7 @@ await queryExecutor.Object.ExecuteQueryAsync( sqltext: string.Empty, parameters: new Dictionary(), dataReaderHandler: null, + dataSourceName:String.Empty, httpContext: null, args: null); }); @@ -242,14 +243,15 @@ Mock queryExecutor It.IsAny(), It.IsAny>(), It.IsAny, Task>>(), + It.IsAny(), It.IsAny(), - It.IsAny>(), - It.IsAny())).CallBase(); + It.IsAny>())).CallBase(); string sqltext = "SELECT * from books"; await queryExecutor.Object.ExecuteQueryAsync( sqltext: sqltext, + dataSourceName: String.Empty, parameters: new Dictionary(), dataReaderHandler: null, args: null); From 7f13366b66b3b08e3305ccb77f83c44d20e36c0d Mon Sep 17 00:00:00 2001 From: Rohan Khanna Date: Mon, 18 Mar 2024 13:45:58 -0700 Subject: [PATCH 4/7] Fixing tests. --- .../Caching/DabCacheServiceIntegrationTests.cs | 12 ++++++------ .../PrimaryKeyTestsForCompositeViews.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs b/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs index e83c670bae..9de2bf4a1e 100644 --- a/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs +++ b/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs @@ -422,9 +422,9 @@ private static Mock CreateMockQueryExecutor(string rawJsonRespon It.IsAny(), It.IsAny>(), It.IsAny?, Task>>(), + It.IsAny(), httpContext, - args, - It.IsAny()).Result) + args).Result) .Returns((JsonElement?)null); break; case ExecutorReturnType.Exception: @@ -432,9 +432,9 @@ private static Mock CreateMockQueryExecutor(string rawJsonRespon It.IsAny(), It.IsAny>(), It.IsAny?, Task>>(), + It.IsAny(), httpContext, - args, - It.IsAny()).Result) + args).Result) .Throws(new DataApiBuilderException( message: "DB ERROR", statusCode: HttpStatusCode.InternalServerError, @@ -446,9 +446,9 @@ private static Mock CreateMockQueryExecutor(string rawJsonRespon It.IsAny(), It.IsAny>(), It.IsAny?, Task>>(), + It.IsAny(), httpContext, - args, - It.IsAny()).Result) + args).Result) .Returns(executorJsonResponse.RootElement.Clone()); break; } diff --git a/src/Service.Tests/SqlTests/RestBootstrapTests/PrimaryKeyTestsForCompositeViews.cs b/src/Service.Tests/SqlTests/RestBootstrapTests/PrimaryKeyTestsForCompositeViews.cs index b37ba5fb54..8d8e81aea2 100644 --- a/src/Service.Tests/SqlTests/RestBootstrapTests/PrimaryKeyTestsForCompositeViews.cs +++ b/src/Service.Tests/SqlTests/RestBootstrapTests/PrimaryKeyTestsForCompositeViews.cs @@ -133,7 +133,7 @@ await SetupAndRunRestApiTest( public async Task TestCleanup() { string dropViewQuery = $"DROP VIEW IF EXISTS {_compositeViewName}"; - await _queryExecutor.ExecuteQueryAsync(dropViewQuery, parameters: null, dataReaderHandler: null); + await _queryExecutor.ExecuteQueryAsync(dropViewQuery, parameters: null, dataReaderHandler: null, dataSourceName: string.Empty); } } } From 8fa0d32148558863e75d4eac92465dc25037e043 Mon Sep 17 00:00:00 2001 From: Rohan Khanna Date: Mon, 18 Mar 2024 13:55:43 -0700 Subject: [PATCH 5/7] Adding description. --- src/Core/Resolvers/IQueryExecutor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Resolvers/IQueryExecutor.cs b/src/Core/Resolvers/IQueryExecutor.cs index f16a5a4e82..8110004444 100644 --- a/src/Core/Resolvers/IQueryExecutor.cs +++ b/src/Core/Resolvers/IQueryExecutor.cs @@ -24,7 +24,7 @@ public interface IQueryExecutor /// in the DbDataReader obtained after executing the query. /// Current request httpContext. /// List of string arguments to the DbDataReader handler. - /// dataSourceName against which to run query. + /// dataSourceName against which to run query. Can specify null or empty to run against default db. /// An object formed using the results of the query as returned by the given handler. public Task ExecuteQueryAsync( string sqltext, From e902202f1f8377e0ca9ef68d5061cc4cb3f171b9 Mon Sep 17 00:00:00 2001 From: Rohan Khanna Date: Mon, 18 Mar 2024 14:00:05 -0700 Subject: [PATCH 6/7] dotnet format. --- src/Service.Tests/Unittests/SqlQueryExecutorUnitTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/Unittests/SqlQueryExecutorUnitTests.cs b/src/Service.Tests/Unittests/SqlQueryExecutorUnitTests.cs index 39b468115e..7ed24f2210 100644 --- a/src/Service.Tests/Unittests/SqlQueryExecutorUnitTests.cs +++ b/src/Service.Tests/Unittests/SqlQueryExecutorUnitTests.cs @@ -194,7 +194,7 @@ await queryExecutor.Object.ExecuteQueryAsync( sqltext: string.Empty, parameters: new Dictionary(), dataReaderHandler: null, - dataSourceName:String.Empty, + dataSourceName: String.Empty, httpContext: null, args: null); }); From 10ee0ebc5495223058d0fe69d7fa5480f43afd5d Mon Sep 17 00:00:00 2001 From: Rohan Khanna Date: Mon, 18 Mar 2024 14:15:07 -0700 Subject: [PATCH 7/7] Fixing tests --- .../Caching/DabCacheServiceIntegrationTests.cs | 4 ++-- .../Unittests/MultiSourceQueryExecutionUnitTests.cs | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs b/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs index 9de2bf4a1e..785c8a572b 100644 --- a/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs +++ b/src/Service.Tests/Caching/DabCacheServiceIntegrationTests.cs @@ -74,7 +74,7 @@ public async Task FirstCacheServiceInvocationCallsFactory() IReadOnlyList actualExecuteQueryAsyncArguments = mockQueryExecutor.Invocations[0].Arguments; Assert.AreEqual(expected: queryMetadata.QueryText, actual: actualExecuteQueryAsyncArguments[0], message: "QueryText " + ERROR_FAILED_ARG_PASSTHROUGH); Assert.AreEqual(expected: queryMetadata.QueryParameters, actual: actualExecuteQueryAsyncArguments[1], message: "Query parameters " + ERROR_FAILED_ARG_PASSTHROUGH); - Assert.AreEqual(expected: queryMetadata.DataSource, actual: actualExecuteQueryAsyncArguments[5], message: "Data source " + ERROR_FAILED_ARG_PASSTHROUGH); + Assert.AreEqual(expected: queryMetadata.DataSource, actual: actualExecuteQueryAsyncArguments[3], message: "Data source " + ERROR_FAILED_ARG_PASSTHROUGH); } /// @@ -217,7 +217,7 @@ public async Task CacheServiceFactoryInvocationReturnsNull() IReadOnlyList actualExecuteQueryAsyncArguments = mockQueryExecutor.Invocations[0].Arguments; Assert.AreEqual(expected: queryMetadata.QueryText, actual: actualExecuteQueryAsyncArguments[0], message: "QueryText " + ERROR_FAILED_ARG_PASSTHROUGH); Assert.AreEqual(expected: queryMetadata.QueryParameters, actual: actualExecuteQueryAsyncArguments[1], message: "Query parameters " + ERROR_FAILED_ARG_PASSTHROUGH); - Assert.AreEqual(expected: queryMetadata.DataSource, actual: actualExecuteQueryAsyncArguments[5], message: "Data source " + ERROR_FAILED_ARG_PASSTHROUGH); + Assert.AreEqual(expected: queryMetadata.DataSource, actual: actualExecuteQueryAsyncArguments[3], message: "Data source " + ERROR_FAILED_ARG_PASSTHROUGH); // Validate that the null value retrned by the factory method is propogated through to and returned by the cache service. Assert.AreEqual(expected: null, actual: result, message: "Expected factory to return a null result."); diff --git a/src/Service.Tests/Unittests/MultiSourceQueryExecutionUnitTests.cs b/src/Service.Tests/Unittests/MultiSourceQueryExecutionUnitTests.cs index be899cc343..b3f2d29f4c 100644 --- a/src/Service.Tests/Unittests/MultiSourceQueryExecutionUnitTests.cs +++ b/src/Service.Tests/Unittests/MultiSourceQueryExecutionUnitTests.cs @@ -12,8 +12,6 @@ using Azure.DataApiBuilder.Core.Models; using Azure.DataApiBuilder.Core.Resolvers; using Azure.DataApiBuilder.Core.Resolvers.Factories; -using Azure.DataApiBuilder.Core.Services; -using Azure.DataApiBuilder.Core.Services.MetadataProviders; using Azure.DataApiBuilder.Service.GraphQLBuilder.Directives; using Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; using Azure.DataApiBuilder.Service.Services; @@ -121,12 +119,8 @@ public async Task TestMultiSourceQuery() Mock mockLoader = new(null); mockLoader.Setup(x => x.TryLoadKnownConfig(out mockConfig1, It.IsAny(), It.IsAny())).Returns(true); - Mock queryManagerFactory = new(); - Mock> logger = new(); RuntimeConfigProvider provider = new(mockLoader.Object); - IMetadataProviderFactory metadataProviderFactory = new MetadataProviderFactory(provider, queryManagerFactory.Object, logger.Object, null); - // Using a sample schema file to test multi-source query. // Schema file contains some sample entities that we can test against. string graphQLSchema = File.ReadAllText("MultiSourceTestSchema.gql");