From 43c920ecb42020fa2a1be663b510728dd9b53ef9 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Fri, 24 Apr 2026 15:00:07 -0700 Subject: [PATCH 1/3] Fix bug --- .../MsSqlMetadataProvider.cs | 2 +- .../Configuration/ConfigurationTests.cs | 129 ++++++++++++++++-- 2 files changed, 121 insertions(+), 10 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 23d12ec31f..1f16c81e28 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -337,7 +337,7 @@ protected override async Task GenerateAutoentitiesIntoEntities(IReadOnlyDictiona // Currently the source type is always Table for auto-generated entities from database objects. Entity generatedEntity = new( Source: new EntitySource( - Object: objectName, + Object: $"{schemaName}.{objectName}", Type: EntitySourceType.Table, Parameters: null, KeyFields: null), diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index e9182927ae..3f896d6184 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -5446,10 +5446,10 @@ public async Task TestGraphQLIntrospectionQueriesAreNotImpactedByDepthLimit() } /// - /// + /// Ensures that autoentities are properly generated into in-memory entities /// - /// - /// + /// Boolean that indicates if we should also use regular entities from config + /// The expected number of entities /// [TestCategory(TestCategory.MSSQL)] [DataTestMethod] @@ -5598,13 +5598,124 @@ public async Task TestAutoentitiesAreGeneratedIntoEntities(bool useEntities, int } /// - /// + /// Ensures that autoentities are properly generated into in-memory entities when entities have non-default schemas. /// - /// - /// - /// - /// - /// + /// The pattern to include for autoentities + /// Boolean that indicates if the pattern is for the foo schema + /// + [TestCategory(TestCategory.MSSQL)] + [DataTestMethod] + [DataRow("foo.%", true, DisplayName = "Test Autoentities with foo schema")] + [DataRow("bar.%", false, DisplayName = "Test Autoentities with bar schema")] + public async Task TestAutoentitiesGeneratedWithDifferentSchemas(string includePattern, bool isPatternFoo) + { + // Arrange + Dictionary autoentityMap = new() + { + { + "PublisherAutoEntity", new Autoentity( + Patterns: new AutoentityPatterns( + Include: new[] { includePattern }, + Exclude: null, + Name: null + ), + Template: new AutoentityTemplate( + Rest: new EntityRestOptions(Enabled: true), + 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(new Dictionary()), + Autoentities: new RuntimeAutoentities(autoentityMap) + ); + + File.WriteAllText(CUSTOM_CONFIG_FILENAME, configuration.ToJson()); + + string[] args = new[] { $"--ConfigFileName={CUSTOM_CONFIG_FILENAME}" }; + using (TestServer server = new(Program.CreateWebHostBuilder(args))) + using (HttpClient client = server.CreateClient()) + { + // Act + RuntimeConfigProvider configProvider = server.Services.GetService(); + using HttpRequestMessage restRequest = new(HttpMethod.Get, "/api/magazines"); + using HttpResponseMessage restResponse = await client.SendAsync(restRequest); + + string item = isPatternFoo ? "title" : "comic_name"; + string graphqlQuery = $@" + {{ + magazines {{ + items {{ + {item} + }} + }} + }}"; + + object graphqlPayload = new { query = graphqlQuery }; + HttpRequestMessage graphqlRequest = new(HttpMethod.Post, "/graphql") + { + Content = JsonContent.Create(graphqlPayload) + }; + HttpResponseMessage graphqlResponse = await client.SendAsync(graphqlRequest); + + // Assert + string expectedResponseFragment = isPatternFoo ? @"{""title"":""Vogue""}" : @"{""comic_name"":""NotVogue""}"; + + // Verify REST response + Assert.AreEqual(HttpStatusCode.OK, restResponse.StatusCode, "REST request to auto-generated entity should succeed"); + + string restResponseBody = await restResponse.Content.ReadAsStringAsync(); + Assert.IsTrue(!string.IsNullOrEmpty(restResponseBody), "REST response should contain data"); + Assert.IsTrue(restResponseBody.Contains(expectedResponseFragment)); + + // Verify GraphQL response + Assert.AreEqual(HttpStatusCode.OK, graphqlResponse.StatusCode, "GraphQL request to auto-generated entity should succeed"); + + string graphqlResponseBody = await graphqlResponse.Content.ReadAsStringAsync(); + Assert.IsTrue(!string.IsNullOrEmpty(graphqlResponseBody), "GraphQL response should contain data"); + Assert.IsFalse(graphqlResponseBody.Contains("errors"), "GraphQL response should not contain errors"); + Assert.IsTrue(graphqlResponseBody.Contains(expectedResponseFragment)); + } + } + + /// + /// Tests that DAB fails if the entities generated from autoentities property + /// do not contain unique parameters such as rest path, graphql singular/plural names, + /// or if the autoentity pattern conflicts with an existing entity name. + /// + /// Definition name of the generated entity from autoentities + /// GraphQL singular name of the generated entity from autoentities + /// GraphQL plural name of the generated entity from autoentities + /// REST path of the generated entity from autoentities + /// Expected exception message /// [TestCategory(TestCategory.MSSQL)] [DataTestMethod] From bf15ba7417f59a4358908db0df00eb0e008b87d7 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Fri, 24 Apr 2026 15:26:56 -0700 Subject: [PATCH 2/3] Address copilot comments --- 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 3e552cfe84..21ae36c009 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -5703,7 +5703,6 @@ public async Task TestAutoentitiesGeneratedWithDifferentSchemas(string includePa using (HttpClient client = server.CreateClient()) { // Act - RuntimeConfigProvider configProvider = server.Services.GetService(); using HttpRequestMessage restRequest = new(HttpMethod.Get, "/api/magazines"); using HttpResponseMessage restResponse = await client.SendAsync(restRequest); From 6618beccf4a1b929fe625eab1ca1ad7c875e5516 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Fri, 24 Apr 2026 15:39:41 -0700 Subject: [PATCH 3/3] Fix failing test --- 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 21ae36c009..be2dcca84f 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -5724,7 +5724,7 @@ public async Task TestAutoentitiesGeneratedWithDifferentSchemas(string includePa HttpResponseMessage graphqlResponse = await client.SendAsync(graphqlRequest); // Assert - string expectedResponseFragment = isPatternFoo ? @"{""title"":""Vogue""}" : @"{""comic_name"":""NotVogue""}"; + string expectedResponseFragment = isPatternFoo ? @"""title"":""Vogue""" : @"""comic_name"":""NotVogue"""; // Verify REST response Assert.AreEqual(HttpStatusCode.OK, restResponse.StatusCode, "REST request to auto-generated entity should succeed");