Skip to content

Commit 6925175

Browse files
aaronburtleCopilotsouvikghosh04CopilotAniruddh25
authored
Cherry-pick for release/2.0: REST/OpenAPI/CLI/config-validation bug fixes (#3516)
### Why make this change? Closes #3515 Ports a set of REST, OpenAPI, CLI, and configuration-validation bug fixes from main to release/2.0 so the 2.0 release line includes: * Correct HTTP semantics for invalid If-Match headers on REST PUT/PATCH (now returns 400 instead of mishandling). * Cleaner OpenAPI documents — descriptive 404 message when filtering by a missing role, and removal of redundant _NoAutoPK / _NoPK schemas when request-body-strict is false. * Accurate config validation: corrected JSON-type mapping for int system types, alignment of parent/child config behavior during deserialization and startup, and improved entity validation logic. * Schema completeness: level-2 property on runtime.cache in dab.draft.schema.json. * CLI quality-of-life: consistent log labels matching ASP.NET Core's default console formatter, and clearer error messaging from dab validate when a logger is not yet available. ### What is this change? Cherry-picked PRs (chronological order on main): * Add level-2 property to runtime.cache in dab.draft.schema.json #3317 * Make CLI Log Labels consistent with ASP.NET Core and use appropriate label on startup #3307 * Align behavior between Parent and Child configs during deserialization and startup #3321 * Add descriptive message when OpenAPI role filter returns 404 #3305 * Return 400 for invalid If-Match values #3415 * Fix dab validate error messaging when logger is not available #3311 * Omit redundant _NoAutoPK and _NoPK OpenAPI schemas when request-body-strict is false #3325 * Map int types to integer in System Type to Json Type Map #3327 * Update config validation logic for entities #3306 ### How was this tested? Existing and newly added unit/integration tests from each source PR. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Souvik Ghosh <souvikofficial04@gmail.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Aniruddh Munde <anmunde@microsoft.com> Co-authored-by: Anusha Kolan <anushakolan10@gmail.com> Co-authored-by: RubenCerna2079 <32799214+RubenCerna2079@users.noreply.github.com>
1 parent aee624b commit 6925175

34 files changed

Lines changed: 1500 additions & 184 deletions

schemas/dab.draft.schema.json

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,40 @@
492492
"ttl-seconds": {
493493
"type": "integer",
494494
"description": "Time to live in seconds",
495-
"default": 5
495+
"default": 5,
496+
"minimum": 1
497+
},
498+
"level-2": {
499+
"type": "object",
500+
"description": "Configuration for the level 2 (distributed) cache and backplane.",
501+
"additionalProperties": false,
502+
"properties": {
503+
"enabled": {
504+
"$ref": "#/$defs/boolean-or-string",
505+
"description": "Enable or disable the level 2 distributed cache.",
506+
"default": false
507+
},
508+
"provider": {
509+
"type": "string",
510+
"description": "The provider for the L2 cache. Currently only 'redis' is supported."
511+
},
512+
"connection-string": {
513+
"type": "string",
514+
"description": "The connection string for the level 2 cache provider."
515+
},
516+
"partition": {
517+
"type": "string",
518+
"description": "The prefix to use for cache keys in level 2 and backplane, useful in a shared environment to avoid collisions."
519+
}
520+
},
521+
"if": {
522+
"properties": {
523+
"enabled": { "const": true }
524+
}
525+
},
526+
"then": {
527+
"required": ["connection-string"]
528+
}
496529
}
497530
}
498531
},

src/Cli.Tests/ConfigureOptionsTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ public void TestDatabaseTypeUpdate(string dbType)
811811
string updatedConfig = _fileSystem!.File.ReadAllText(TEST_RUNTIME_CONFIG_FILE);
812812
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(updatedConfig, out RuntimeConfig? config));
813813
Assert.IsNotNull(config.Runtime);
814-
Assert.AreEqual(config.DataSource.DatabaseType, Enum.Parse<DatabaseType>(dbType, ignoreCase: true));
814+
Assert.AreEqual(config.DataSource!.DatabaseType, Enum.Parse<DatabaseType>(dbType, ignoreCase: true));
815815
}
816816

817817
/// <summary>
@@ -841,7 +841,7 @@ public void TestDatabaseTypeUpdateCosmosDB_NoSQLToMSSQL()
841841
string updatedConfig = _fileSystem!.File.ReadAllText(TEST_RUNTIME_CONFIG_FILE);
842842
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(updatedConfig, out RuntimeConfig? config));
843843
Assert.IsNotNull(config.Runtime);
844-
Assert.AreEqual(config.DataSource.DatabaseType, DatabaseType.MSSQL);
844+
Assert.AreEqual(config.DataSource!.DatabaseType, DatabaseType.MSSQL);
845845
Assert.AreEqual(config.DataSource.Options!.GetValueOrDefault("set-session-context", false), true);
846846
Assert.IsFalse(config.DataSource.Options!.ContainsKey("database"));
847847
Assert.IsFalse(config.DataSource.Options!.ContainsKey("container"));
@@ -877,7 +877,7 @@ public void TestDatabaseTypeUpdateMSSQLToCosmosDB_NoSQL()
877877
string updatedConfig = _fileSystem!.File.ReadAllText(TEST_RUNTIME_CONFIG_FILE);
878878
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(updatedConfig, out RuntimeConfig? config));
879879
Assert.IsNotNull(config.Runtime);
880-
Assert.AreEqual(config.DataSource.DatabaseType, DatabaseType.CosmosDB_NoSQL);
880+
Assert.AreEqual(config.DataSource!.DatabaseType, DatabaseType.CosmosDB_NoSQL);
881881
Assert.AreEqual(config.DataSource.Options!.GetValueOrDefault("database"), "testdb");
882882
Assert.AreEqual(config.DataSource.Options!.GetValueOrDefault("container"), "testcontainer");
883883
Assert.AreEqual(config.DataSource.Options!.GetValueOrDefault("schema"), "testschema.gql");

src/Cli.Tests/EndToEndTests.cs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public Task TestInitForCosmosDBNoSql()
6565

6666
Assert.IsNotNull(runtimeConfig);
6767
Assert.IsTrue(runtimeConfig.AllowIntrospection);
68-
Assert.AreEqual(DatabaseType.CosmosDB_NoSQL, runtimeConfig.DataSource.DatabaseType);
68+
Assert.AreEqual(DatabaseType.CosmosDB_NoSQL, runtimeConfig.DataSource!.DatabaseType);
6969
CosmosDbNoSQLDataSourceOptions? cosmosDataSourceOptions = runtimeConfig.DataSource.GetTypedOptions<CosmosDbNoSQLDataSourceOptions>();
7070
Assert.IsNotNull(cosmosDataSourceOptions);
7171
Assert.AreEqual("graphqldb", cosmosDataSourceOptions.Database);
@@ -93,7 +93,7 @@ public void TestInitForCosmosDBPostgreSql()
9393
Assert.IsTrue(_runtimeConfigLoader!.TryLoadConfig(TEST_RUNTIME_CONFIG_FILE, out RuntimeConfig? runtimeConfig));
9494

9595
Assert.IsNotNull(runtimeConfig);
96-
Assert.AreEqual(DatabaseType.CosmosDB_PostgreSQL, runtimeConfig.DataSource.DatabaseType);
96+
Assert.AreEqual(DatabaseType.CosmosDB_PostgreSQL, runtimeConfig.DataSource!.DatabaseType);
9797
Assert.IsNotNull(runtimeConfig.Runtime);
9898
Assert.IsNotNull(runtimeConfig.Runtime.Rest);
9999
Assert.AreEqual("/rest-api", runtimeConfig.Runtime.Rest.Path);
@@ -124,7 +124,7 @@ public void TestInitializingRestAndGraphQLGlobalSettings()
124124
out RuntimeConfig? runtimeConfig,
125125
replacementSettings: replacementSettings));
126126

127-
SqlConnectionStringBuilder builder = new(runtimeConfig.DataSource.ConnectionString);
127+
SqlConnectionStringBuilder builder = new(runtimeConfig.DataSource!.ConnectionString);
128128
Assert.AreEqual(ProductInfo.GetDataApiBuilderUserAgent(), builder.ApplicationName);
129129

130130
Assert.IsNotNull(runtimeConfig);
@@ -205,7 +205,7 @@ public void TestEnablingMultipleCreateOperation(CliBool isMultipleCreateEnabled,
205205
replacementSettings: replacementSettings));
206206

207207
Assert.IsNotNull(runtimeConfig);
208-
Assert.AreEqual(expectedDbType, runtimeConfig.DataSource.DatabaseType);
208+
Assert.AreEqual(expectedDbType, runtimeConfig.DataSource!.DatabaseType);
209209
Assert.IsNotNull(runtimeConfig.Runtime);
210210
Assert.IsNotNull(runtimeConfig.Runtime.GraphQL);
211211
if (runtimeConfig.DataSource.DatabaseType is DatabaseType.MSSQL && isMultipleCreateEnabled is not CliBool.None)
@@ -244,7 +244,7 @@ public void TestAddEntity()
244244

245245
Assert.IsTrue(_runtimeConfigLoader!.TryLoadConfig(TEST_RUNTIME_CONFIG_FILE, out RuntimeConfig? addRuntimeConfig));
246246
Assert.IsNotNull(addRuntimeConfig);
247-
Assert.AreEqual(TEST_ENV_CONN_STRING, addRuntimeConfig.DataSource.ConnectionString);
247+
Assert.AreEqual(TEST_ENV_CONN_STRING, addRuntimeConfig.DataSource!.ConnectionString);
248248
Assert.AreEqual(1, addRuntimeConfig.Entities.Count()); // 1 new entity added
249249
Assert.IsTrue(addRuntimeConfig.Entities.ContainsKey("todo"));
250250
Entity entity = addRuntimeConfig.Entities["todo"];
@@ -1173,10 +1173,6 @@ public async Task TestExitOfRuntimeEngineWithInvalidConfig(
11731173
Assert.IsNotNull(output);
11741174
StringAssert.Contains(output, $"Deserialization of the configuration file failed.", StringComparison.Ordinal);
11751175

1176-
output = await process.StandardOutput.ReadLineAsync();
1177-
Assert.IsNotNull(output);
1178-
StringAssert.Contains(output, $"Error: Failed to parse the config file: {TEST_RUNTIME_CONFIG_FILE}.", StringComparison.Ordinal);
1179-
11801176
output = await process.StandardOutput.ReadLineAsync();
11811177
Assert.IsNotNull(output);
11821178
StringAssert.Contains(output, $"Failed to start the engine.", StringComparison.Ordinal);

src/Cli.Tests/EnvironmentTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ public async Task FailureToStartEngineWhenEnvVarNamedWrong()
163163
);
164164

165165
string? output = await process.StandardError.ReadLineAsync();
166-
Assert.AreEqual("Deserialization of the configuration file failed during a post-processing step.", output);
167-
output = await process.StandardError.ReadToEndAsync();
166+
Assert.IsNotNull(output);
167+
// Clean error message on stderr with no stack trace.
168168
StringAssert.Contains(output, "A valid Connection String should be provided.", StringComparison.Ordinal);
169169
process.Kill();
170170
}

src/Cli.Tests/ModuleInitializer.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ public static void Init()
119119
VerifierSettings.IgnoreMember<DataSource>(dataSource => dataSource.DatabaseTypeNotSupportedMessage);
120120
// Ignore DefaultDataSourceName as that's not serialized in our config file.
121121
VerifierSettings.IgnoreMember<RuntimeConfig>(config => config.DefaultDataSourceName);
122+
// Ignore IsRootConfig as that's a computed property for validation, not serialized.
123+
VerifierSettings.IgnoreMember<RuntimeConfig>(config => config.IsRootConfig);
124+
// Ignore IsChildConfig as that's a runtime flag for validation, not serialized.
125+
VerifierSettings.IgnoreMember<RuntimeConfig>(config => config.IsChildConfig);
126+
// Ignore AutoentityResolutionCounts as that's populated at runtime during metadata initialization.
127+
VerifierSettings.IgnoreMember<RuntimeConfig>(config => config.AutoentityResolutionCounts);
128+
// Ignore ChildConfigs as that's populated at runtime during child config loading.
129+
VerifierSettings.IgnoreMember<RuntimeConfig>(config => config.ChildConfigs);
122130
// Ignore MaxResponseSizeMB as as that's unimportant from a test standpoint.
123131
VerifierSettings.IgnoreMember<HostOptions>(options => options.MaxResponseSizeMB);
124132
// Ignore UserProvidedMaxResponseSizeMB as that's not serialized in our config file.

src/Cli.Tests/UserDelegatedAuthRuntimeParsingTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public void TestRuntimeCanParseUserDelegatedAuthConfig()
5050
// Assert
5151
Assert.IsTrue(success);
5252
Assert.IsNotNull(config);
53-
Assert.IsNotNull(config.DataSource.UserDelegatedAuth);
53+
Assert.IsNotNull(config.DataSource!.UserDelegatedAuth);
5454
Assert.IsTrue(config.DataSource.UserDelegatedAuth.Enabled);
5555
Assert.AreEqual("https://database.windows.net", config.DataSource.UserDelegatedAuth.DatabaseAudience);
5656
}
@@ -95,7 +95,7 @@ public void TestRuntimeCanParseConfigWithoutUserDelegatedAuth()
9595
// Assert
9696
Assert.IsTrue(success);
9797
Assert.IsNotNull(config);
98-
Assert.IsNull(config.DataSource.UserDelegatedAuth);
98+
Assert.IsNull(config.DataSource!.UserDelegatedAuth);
9999
}
100100
}
101101
}

0 commit comments

Comments
 (0)