Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 15 additions & 7 deletions schemas/dab.draft.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -819,15 +819,23 @@
"additionalProperties": false,
"properties": {
"mcp": {
"type": "object",
"description": "MCP endpoint configuration",
"additionalProperties": false,
"properties": {
"dml-tools": {
"oneOf": [
{
"$ref": "#/$defs/boolean-or-string",
"description": "Enable/disable all DML tools with default settings."
"description": "Boolean shorthand: true enables dml-tools only (custom-tool remains false), false disables all MCP functionality."
},
{
"type": "object",
"description": "MCP endpoint configuration",
"additionalProperties": false,
"properties": {
"dml-tools": {
"$ref": "#/$defs/boolean-or-string",
"description": "Enable/disable all DML tools with default settings."
}
}
}
}
]
},
"rest": {
"type": "object",
Expand Down
4 changes: 2 additions & 2 deletions src/Cli.Tests/AutoConfigTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public void TestConfigureAutoentitiesDefinition_WithTemplateOptions()
definitionName: "test-def",
templateRestEnabled: true,
templateGraphqlEnabled: false,
templateMcpDmlTool: "true",
templateMcpDmlTools: "true",
templateCacheEnabled: true,
templateCacheTtlSeconds: 30,
templateCacheLevel: "L1",
Expand Down Expand Up @@ -196,7 +196,7 @@ public void TestConfigureAutoentitiesDefinition_InvalidMcpDmlTool()

AutoConfigOptions options = new(
definitionName: "test-def",
templateMcpDmlTool: "invalid-value",
templateMcpDmlTools: "invalid-value",
permissions: new[] { "anonymous", "read" },
config: TEST_RUNTIME_CONFIG_FILE
);
Expand Down
78 changes: 78 additions & 0 deletions src/Cli.Tests/EndToEndTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using Azure.DataApiBuilder.Config.Converters;
using Azure.DataApiBuilder.Product;
using Azure.DataApiBuilder.Service;
using Cli.Constants;
using Microsoft.Data.SqlClient;

Expand Down Expand Up @@ -857,6 +858,83 @@ public void TestEngineStartUpWithVerboseAndLogLevelOptions(string logLevelOption
StringAssert.Contains(output, $"User provided config file: {TEST_RUNTIME_CONFIG_FILE}", StringComparison.Ordinal);
}

/// <summary>
/// Validates that `dab start` correctly sets <see cref="Startup.IsLogLevelOverriddenByCli"/>
/// based on whether the --LogLevel CLI flag is provided.
///
/// When the --LogLevel flag is provided, IsLogLevelOverriddenByCli should be true.
/// When the --LogLevel flag is omitted (log level comes from the config file), IsLogLevelOverriddenByCli should be false.
/// </summary>
/// <param name="cliLogLevel">The --LogLevel CLI flag value, or null to omit the flag.</param>
/// <param name="expectedIsOverridden">Expected value of Startup.IsLogLevelOverriddenByCli.</param>
[DataTestMethod]
[DataRow(null, false, DisplayName = "IsLogLevelOverriddenByCli is false")]
[DataRow(LogLevel.Error, true, DisplayName = "IsLogLevelOverriddenByCli is true")]
public async Task TestStartCommandResolvesLogLevelFromConfigOrFlag(
LogLevel? cliLogLevel,
bool expectedIsOverridden)
{
string baseConfig = @"
{
""$schema"": """ + DAB_DRAFT_SCHEMA_TEST_PATH + @""",
""data-source"": {
""database-type"": ""mssql"",
""connection-string"": """ + SAMPLE_TEST_CONN_STRING + @"""
},
""runtime"": {
""rest"": {
""path"": ""/api"",
""enabled"": true
},
""graphql"": {
""path"": ""/graphql"",
""enabled"": true,
""allow-introspection"": true
},
""host"": {
""mode"": ""development"",
""cors"": {
""origins"": [],
""allow-credentials"": false
},
""authentication"": {
""provider"": ""Unauthenticated""
}
},
""telemetry"": {
""log-level"": {
""Azure.DataApiBuilder.Core.Services.ISqlMetadataProvider"": ""Information"",
""Azure.DataApiBuilder.Core"": ""Debug"",
""Azure.DataApiBuilder.Service.Controllers.RestController"": ""Error"",
""default"": ""Warning""
}
}
},
""entities"": {}
}";

// Merge in an entity so the config is not rejected for having an empty entities section.
string configWithLogLevel = AddPropertiesToJson(baseConfig, BASIC_ENTITY_WITH_ANONYMOUS_ROLE);
_fileSystem!.File.WriteAllText(TEST_RUNTIME_CONFIG_FILE, configWithLogLevel);

StartOptions options = new(
verbose: false,
logLevel: cliLogLevel,
isHttpsRedirectionDisabled: false,
mcpStdio: false,
mcpRole: null,
config: TEST_RUNTIME_CONFIG_FILE);

// Run TryStartEngineWithOptions on a background task because StartEngine blocks until the host shuts down.
Task engineTask = Task.Run(() =>
TryStartEngineWithOptions(options, _runtimeConfigLoader!, _fileSystem!));

// Wait for the engine to finish loading the config.
await Task.Delay(TimeSpan.FromSeconds(5));

Assert.AreEqual(expectedIsOverridden, Startup.IsLogLevelOverriddenByCli);
}
Comment thread
RubenCerna2079 marked this conversation as resolved.

/// <summary>
/// Validates that valid usage of verbs and associated options produce exit code 0 (CliReturnCode.SUCCESS).
/// Verifies that explicitly implemented verbs (add, update, init, start) and appropriately
Expand Down
2 changes: 2 additions & 0 deletions src/Cli.Tests/ModuleInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public static void Init()
VerifierSettings.IgnoreMember<Entity>(entity => entity.EntityFirst);
// Ignore the entity IsLinkingEntity as that's unimportant from a test standpoint.
VerifierSettings.IgnoreMember<Entity>(entity => entity.IsLinkingEntity);
// Ignore the entity IsAutoentity as that's unimportant from a test standpoint.
VerifierSettings.IgnoreMember<Entity>(entity => entity.IsAutoentity);
// Ignore the UserProvidedTtlOptions. They aren't serialized to our config file, enforced by EntityCacheOptionsConverter.
VerifierSettings.IgnoreMember<EntityCacheOptions>(cacheOptions => cacheOptions.UserProvidedTtlOptions);
// Ignore the UserProvidedEnabledOptions. They aren't serialized to our config file, enforced by EntityCacheOptionsConverter.
Expand Down
8 changes: 4 additions & 4 deletions src/Cli/Commands/AutoConfigOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public AutoConfigOptions(
IEnumerable<string>? patternsInclude = null,
IEnumerable<string>? patternsExclude = null,
string? patternsName = null,
string? templateMcpDmlTool = null,
string? templateMcpDmlTools = null,
bool? templateRestEnabled = null,
bool? templateGraphqlEnabled = null,
bool? templateCacheEnabled = null,
Expand All @@ -39,7 +39,7 @@ public AutoConfigOptions(
PatternsInclude = patternsInclude;
PatternsExclude = patternsExclude;
PatternsName = patternsName;
TemplateMcpDmlTool = templateMcpDmlTool;
TemplateMcpDmlTools = templateMcpDmlTools;
TemplateRestEnabled = templateRestEnabled;
TemplateGraphqlEnabled = templateGraphqlEnabled;
TemplateCacheEnabled = templateCacheEnabled;
Expand All @@ -61,8 +61,8 @@ public AutoConfigOptions(
[Option("patterns.name", Required = false, HelpText = "Interpolation syntax for entity naming (must be unique for each generated entity). Default: '{object}'")]
public string? PatternsName { get; }

[Option("template.mcp.dml-tool", Required = false, HelpText = "Enable/disable DML tools for generated entities. Allowed values: true, false. Default: true")]
public string? TemplateMcpDmlTool { get; }
[Option("template.mcp.dml-tools", Required = false, HelpText = "Enable/disable DML tools for generated entities. Allowed values: true, false. Default: true")]
public string? TemplateMcpDmlTools { get; }

[Option("template.rest.enabled", Required = false, HelpText = "Enable/disable REST endpoint for generated entities. Allowed values: true, false. Default: true")]
public bool? TemplateRestEnabled { get; }
Expand Down
24 changes: 8 additions & 16 deletions src/Cli/ConfigGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2596,18 +2596,10 @@ public static bool TryStartEngineWithOptions(StartOptions options, FileSystemRun
}

minimumLogLevel = (LogLevel)options.LogLevel;
args.Add("--LogLevel");
args.Add(minimumLogLevel.ToString());
_logger.LogInformation("Setting minimum LogLevel: {minimumLogLevel}.", minimumLogLevel);
}
else
{
minimumLogLevel = deserializedRuntimeConfig.GetConfiguredLogLevel();
HostMode hostModeType = deserializedRuntimeConfig.IsDevelopmentMode() ? HostMode.Development : HostMode.Production;

_logger.LogInformation($"Setting default minimum LogLevel: {minimumLogLevel} for {hostModeType} mode.", minimumLogLevel, hostModeType);
}

args.Add("--LogLevel");
args.Add(minimumLogLevel.ToString());

// This will add args to disable automatic redirects to https if specified by user
if (options.IsHttpsRedirectionDisabled)
Expand Down Expand Up @@ -3109,11 +3101,11 @@ private static AutoentityPatterns BuildAutoentityPatterns(AutoConfigOptions opti
bool userProvidedCache = existingAutoentity?.Template.UserProvidedCacheOptions ?? false;

// Update MCP options
if (!string.IsNullOrWhiteSpace(options.TemplateMcpDmlTool))
if (!string.IsNullOrWhiteSpace(options.TemplateMcpDmlTools))
{
if (!bool.TryParse(options.TemplateMcpDmlTool, out bool mcpDmlToolValue))
if (!bool.TryParse(options.TemplateMcpDmlTools, out bool mcpDmlToolValue))
{
_logger.LogError("Invalid value for template.mcp.dml-tool: {value}. Valid values are: true, false", options.TemplateMcpDmlTool);
_logger.LogError("Invalid value for template.mcp.dml-tools: {value}. Valid values are: true, false", options.TemplateMcpDmlTools);
return null;
}

Expand All @@ -3122,7 +3114,7 @@ private static AutoentityPatterns BuildAutoentityPatterns(AutoConfigOptions opti
bool? dmlToolValue = mcpDmlToolValue;
mcp = new EntityMcpOptions(customToolEnabled: customToolEnabled, dmlToolsEnabled: dmlToolValue);
userProvidedMcp = true;
_logger.LogInformation("Updated template.mcp.dml-tool for definition '{DefinitionName}'", options.DefinitionName);
_logger.LogInformation("Updated template.mcp.dml-tools for definition '{DefinitionName}'", options.DefinitionName);
}

// Update REST options
Expand Down Expand Up @@ -3268,7 +3260,7 @@ public static bool TrySimulateAutoentities(AutoConfigSimulateOptions options, Fi

if (runtimeConfig.DataSource.DatabaseType != DatabaseType.MSSQL)
{
_logger.LogError("Autoentities simulation is only supported for MSSQL databases. Current database type: {DatabaseType}.", runtimeConfig.DataSource.DatabaseType);
_logger.LogError("The autoentities simulation is only supported for MSSQL databases. Current database type: {DatabaseType}.", runtimeConfig.DataSource.DatabaseType);
return false;
}

Expand Down Expand Up @@ -3360,7 +3352,7 @@ public static bool TrySimulateAutoentities(AutoConfigSimulateOptions options, Fi
/// <param name="results">The simulation results keyed by filter (definition) name.</param>
private static void WriteSimulationResultsToConsole(Dictionary<string, List<(string EntityName, string SchemaName, string ObjectName)>> results)
{
Console.WriteLine("AutoEntities Simulation Results");
Console.WriteLine("Autoentities Simulation Results");
Console.WriteLine();

foreach ((string filterName, List<(string EntityName, string SchemaName, string ObjectName)> matches) in results)
Expand Down
7 changes: 6 additions & 1 deletion src/Config/ObjectModel/Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public record Entity
[JsonIgnore]
public bool IsLinkingEntity { get; init; }

[JsonIgnore]
public bool IsAutoentity { get; init; }

[JsonConstructor]
public Entity(
EntitySource Source,
Expand All @@ -58,7 +61,8 @@ public Entity(
bool IsLinkingEntity = false,
EntityHealthCheckConfig? Health = null,
string? Description = null,
EntityMcpOptions? Mcp = null)
EntityMcpOptions? Mcp = null,
bool IsAutoentity = false)
{
this.Health = Health;
this.Source = Source;
Expand All @@ -72,6 +76,7 @@ public Entity(
this.IsLinkingEntity = IsLinkingEntity;
this.Description = Description;
this.Mcp = Mcp;
this.IsAutoentity = IsAutoentity;
}

/// <summary>
Expand Down
7 changes: 6 additions & 1 deletion src/Config/ObjectModel/RuntimeConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,11 @@ public bool TryAddGeneratedAutoentityNameToDataSourceName(string entityName, str
return false;
}

public bool RemoveGeneratedAutoentityNameFromDataSourceName(string entityName)
{
return _entityNameToDataSourceName.Remove(entityName);
}

/// <summary>
/// Constructor for runtimeConfig.
/// To be used when setting up from cli json scenario.
Expand Down Expand Up @@ -502,7 +507,7 @@ public string GetDataSourceNameFromAutoentityName(string autoentityName)
if (!_autoentityNameToDataSourceName.TryGetValue(autoentityName, out string? autoentityDataSource))
{
throw new DataApiBuilderException(
message: $"{autoentityName} is not a valid autoentity.",
message: $"'{autoentityName}' is not a valid autoentities definition.",
statusCode: HttpStatusCode.NotFound,
subStatusCode: DataApiBuilderException.SubStatusCodes.EntityNotFound);
}
Expand Down
28 changes: 28 additions & 0 deletions src/Core/Configurations/RuntimeConfigProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -426,4 +426,32 @@ public void AddMergedEntitiesToConfig(Dictionary<string, Entity> newEntities)
};
_configLoader.EditRuntimeConfig(newRuntimeConfig);
}

public void RemoveGeneratedAutoentitiesFromConfig()
{
Dictionary<string, Entity> entities = new(_configLoader.RuntimeConfig!.Entities);
List<string> removingEntities = new();

// Add entities that will be removed to a list first to avoid modifying the collection while iterating over it.
foreach ((string name, Entity entity) in entities)
{
if (entity.IsAutoentity)
{
removingEntities.Add(name);
}
}

// Remove all autoentities from the config.
foreach (string name in removingEntities)
{
entities.Remove(name);
_configLoader.RuntimeConfig!.RemoveGeneratedAutoentityNameFromDataSourceName(name);
}

RuntimeConfig newRuntimeConfig = _configLoader.RuntimeConfig! with
{
Entities = new(entities)
};
_configLoader.EditRuntimeConfig(newRuntimeConfig);
}
}
Loading
Loading