diff --git a/src/Cli.Tests/AutoentitiesConfigureTests.cs b/src/Cli.Tests/AutoentitiesConfigureTests.cs
new file mode 100644
index 0000000000..a0d1ae9208
--- /dev/null
+++ b/src/Cli.Tests/AutoentitiesConfigureTests.cs
@@ -0,0 +1,305 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.IO.Abstractions;
+using System.Text;
+using System.Text.Json;
+using Azure.DataApiBuilder.Config;
+using Azure.DataApiBuilder.Config.ObjectModel;
+using Cli.Commands;
+using Microsoft.Extensions.Logging;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using static Cli.Utils;
+using static Cli.Tests.TestHelper;
+
+namespace Cli.Tests;
+
+///
+/// Tests for the autoentities-configure CLI command.
+///
+[TestClass]
+public class AutoentitiesConfigureTests
+{
+ private IFileSystem? _fileSystem;
+ private FileSystemRuntimeConfigLoader? _runtimeConfigLoader;
+
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ _fileSystem = FileSystemUtils.ProvisionMockFileSystem();
+ _runtimeConfigLoader = new FileSystemRuntimeConfigLoader(_fileSystem);
+
+ ILoggerFactory loggerFactory = TestLoggerSupport.ProvisionLoggerFactory();
+ ConfigGenerator.SetLoggerForCliConfigGenerator(loggerFactory.CreateLogger());
+ SetCliUtilsLogger(loggerFactory.CreateLogger());
+ }
+
+ [TestCleanup]
+ public void TestCleanup()
+ {
+ _fileSystem = null;
+ _runtimeConfigLoader = null;
+ }
+
+ ///
+ /// Tests that a new autoentities definition is successfully created with patterns.
+ ///
+ [TestMethod]
+ public void TestCreateAutoentitiesDefinition_WithPatterns()
+ {
+ // Arrange
+ InitOptions initOptions = CreateBasicInitOptionsForMsSqlWithConfig(config: TEST_RUNTIME_CONFIG_FILE);
+ Assert.IsTrue(ConfigGenerator.TryGenerateConfig(initOptions, _runtimeConfigLoader!, _fileSystem!));
+
+ AutoentitiesConfigureOptions options = new(
+ definitionName: "test-def",
+ patternsInclude: new[] { "dbo.%", "sys.%" },
+ patternsExclude: new[] { "dbo.internal%" },
+ patternsName: "{schema}_{table}",
+ permissions: new[] { "anonymous", "read" },
+ config: TEST_RUNTIME_CONFIG_FILE
+ );
+
+ // Act
+ bool success = ConfigGenerator.TryConfigureAutoentities(options, _runtimeConfigLoader!, _fileSystem!);
+
+ // Assert
+ Assert.IsTrue(success);
+ Assert.IsTrue(_runtimeConfigLoader!.TryLoadConfig(TEST_RUNTIME_CONFIG_FILE, out RuntimeConfig? config));
+ Assert.IsNotNull(config.Autoentities);
+ Assert.IsTrue(config.Autoentities.AutoEntities.ContainsKey("test-def"));
+
+ Autoentity autoentity = config.Autoentities.AutoEntities["test-def"];
+ Assert.AreEqual(2, autoentity.Patterns.Include.Length);
+ Assert.AreEqual("dbo.%", autoentity.Patterns.Include[0]);
+ Assert.AreEqual("sys.%", autoentity.Patterns.Include[1]);
+ Assert.AreEqual(1, autoentity.Patterns.Exclude.Length);
+ Assert.AreEqual("dbo.internal%", autoentity.Patterns.Exclude[0]);
+ Assert.AreEqual("{schema}_{table}", autoentity.Patterns.Name);
+ }
+
+ ///
+ /// Tests that template options are correctly configured for an autoentities definition.
+ ///
+ [TestMethod]
+ public void TestConfigureAutoentitiesDefinition_WithTemplateOptions()
+ {
+ // Arrange
+ InitOptions initOptions = CreateBasicInitOptionsForMsSqlWithConfig(config: TEST_RUNTIME_CONFIG_FILE);
+ Assert.IsTrue(ConfigGenerator.TryGenerateConfig(initOptions, _runtimeConfigLoader!, _fileSystem!));
+
+ AutoentitiesConfigureOptions options = new(
+ definitionName: "test-def",
+ templateRestEnabled: true,
+ templateGraphqlEnabled: false,
+ templateMcpDmlTool: "true",
+ templateCacheEnabled: true,
+ templateCacheTtlSeconds: 30,
+ templateCacheLevel: "L1",
+ templateHealthEnabled: true,
+ permissions: new[] { "anonymous", "read" },
+ config: TEST_RUNTIME_CONFIG_FILE
+ );
+
+ // Act
+ bool success = ConfigGenerator.TryConfigureAutoentities(options, _runtimeConfigLoader!, _fileSystem!);
+
+ // Assert
+ Assert.IsTrue(success);
+ Assert.IsTrue(_runtimeConfigLoader!.TryLoadConfig(TEST_RUNTIME_CONFIG_FILE, out RuntimeConfig? config));
+
+ Autoentity autoentity = config.Autoentities!.AutoEntities["test-def"];
+ Assert.IsTrue(autoentity.Template.Rest.Enabled);
+ Assert.IsFalse(autoentity.Template.GraphQL.Enabled);
+ Assert.IsTrue(autoentity.Template.Mcp!.DmlToolEnabled);
+ Assert.AreEqual(true, autoentity.Template.Cache.Enabled);
+ Assert.AreEqual(30, autoentity.Template.Cache.TtlSeconds);
+ Assert.AreEqual(EntityCacheLevel.L1, autoentity.Template.Cache.Level);
+ Assert.IsTrue(autoentity.Template.Health.Enabled);
+ }
+
+ ///
+ /// Tests that an existing autoentities definition is successfully updated.
+ ///
+ [TestMethod]
+ public void TestUpdateExistingAutoentitiesDefinition()
+ {
+ // Arrange
+ InitOptions initOptions = CreateBasicInitOptionsForMsSqlWithConfig(config: TEST_RUNTIME_CONFIG_FILE);
+ Assert.IsTrue(ConfigGenerator.TryGenerateConfig(initOptions, _runtimeConfigLoader!, _fileSystem!));
+
+ // Create initial definition
+ AutoentitiesConfigureOptions initialOptions = new(
+ definitionName: "test-def",
+ patternsInclude: new[] { "dbo.%" },
+ templateCacheTtlSeconds: 10,
+ permissions: new[] { "anonymous", "read" },
+ config: TEST_RUNTIME_CONFIG_FILE
+ );
+ Assert.IsTrue(ConfigGenerator.TryConfigureAutoentities(initialOptions, _runtimeConfigLoader!, _fileSystem!));
+
+ // Update definition
+ AutoentitiesConfigureOptions updateOptions = new(
+ definitionName: "test-def",
+ patternsExclude: new[] { "dbo.internal%" },
+ templateCacheTtlSeconds: 60,
+ permissions: new[] { "authenticated", "create,read,update,delete" },
+ config: TEST_RUNTIME_CONFIG_FILE
+ );
+
+ // Act
+ bool success = ConfigGenerator.TryConfigureAutoentities(updateOptions, _runtimeConfigLoader!, _fileSystem!);
+
+ // Assert
+ Assert.IsTrue(success);
+ Assert.IsTrue(_runtimeConfigLoader!.TryLoadConfig(TEST_RUNTIME_CONFIG_FILE, out RuntimeConfig? config));
+
+ Autoentity autoentity = config.Autoentities!.AutoEntities["test-def"];
+ // Include should remain from initial setup
+ Assert.AreEqual(1, autoentity.Patterns.Include.Length);
+ Assert.AreEqual("dbo.%", autoentity.Patterns.Include[0]);
+ // Exclude should be added
+ Assert.AreEqual(1, autoentity.Patterns.Exclude.Length);
+ Assert.AreEqual("dbo.internal%", autoentity.Patterns.Exclude[0]);
+ // Cache TTL should be updated
+ Assert.AreEqual(60, autoentity.Template.Cache.TtlSeconds);
+ // Permissions should be replaced
+ Assert.AreEqual(1, autoentity.Permissions.Length);
+ Assert.AreEqual("authenticated", autoentity.Permissions[0].Role);
+ }
+
+ ///
+ /// Tests that permissions are correctly parsed and applied.
+ ///
+ [TestMethod]
+ public void TestConfigureAutoentitiesDefinition_WithMultipleActions()
+ {
+ // Arrange
+ InitOptions initOptions = CreateBasicInitOptionsForMsSqlWithConfig(config: TEST_RUNTIME_CONFIG_FILE);
+ Assert.IsTrue(ConfigGenerator.TryGenerateConfig(initOptions, _runtimeConfigLoader!, _fileSystem!));
+
+ AutoentitiesConfigureOptions options = new(
+ definitionName: "test-def",
+ permissions: new[] { "authenticated", "create,read,update,delete" },
+ config: TEST_RUNTIME_CONFIG_FILE
+ );
+
+ // Act
+ bool success = ConfigGenerator.TryConfigureAutoentities(options, _runtimeConfigLoader!, _fileSystem!);
+
+ // Assert
+ Assert.IsTrue(success);
+ Assert.IsTrue(_runtimeConfigLoader!.TryLoadConfig(TEST_RUNTIME_CONFIG_FILE, out RuntimeConfig? config));
+
+ Autoentity autoentity = config.Autoentities!.AutoEntities["test-def"];
+ Assert.AreEqual(1, autoentity.Permissions.Length);
+ Assert.AreEqual("authenticated", autoentity.Permissions[0].Role);
+ Assert.AreEqual(4, autoentity.Permissions[0].Actions.Length);
+ }
+
+ ///
+ /// Tests that invalid MCP dml-tool value is handled correctly.
+ ///
+ [TestMethod]
+ public void TestConfigureAutoentitiesDefinition_InvalidMcpDmlTool()
+ {
+ // Arrange
+ InitOptions initOptions = CreateBasicInitOptionsForMsSqlWithConfig(config: TEST_RUNTIME_CONFIG_FILE);
+ Assert.IsTrue(ConfigGenerator.TryGenerateConfig(initOptions, _runtimeConfigLoader!, _fileSystem!));
+
+ AutoentitiesConfigureOptions options = new(
+ definitionName: "test-def",
+ templateMcpDmlTool: "invalid-value",
+ permissions: new[] { "anonymous", "read" },
+ config: TEST_RUNTIME_CONFIG_FILE
+ );
+
+ // Act
+ bool success = ConfigGenerator.TryConfigureAutoentities(options, _runtimeConfigLoader!, _fileSystem!);
+
+ // Assert - Should fail due to invalid MCP value
+ Assert.IsFalse(success);
+ }
+
+ ///
+ /// Tests that invalid cache level value is handled correctly.
+ ///
+ [TestMethod]
+ public void TestConfigureAutoentitiesDefinition_InvalidCacheLevel()
+ {
+ // Arrange
+ InitOptions initOptions = CreateBasicInitOptionsForMsSqlWithConfig(config: TEST_RUNTIME_CONFIG_FILE);
+ Assert.IsTrue(ConfigGenerator.TryGenerateConfig(initOptions, _runtimeConfigLoader!, _fileSystem!));
+
+ AutoentitiesConfigureOptions options = new(
+ definitionName: "test-def",
+ templateCacheLevel: "InvalidLevel",
+ permissions: new[] { "anonymous", "read" },
+ config: TEST_RUNTIME_CONFIG_FILE
+ );
+
+ // Act
+ bool success = ConfigGenerator.TryConfigureAutoentities(options, _runtimeConfigLoader!, _fileSystem!);
+
+ // Assert - Should fail due to invalid cache level
+ Assert.IsFalse(success);
+ }
+
+ ///
+ /// Tests that multiple autoentities definitions can coexist.
+ ///
+ [TestMethod]
+ public void TestMultipleAutoentitiesDefinitions()
+ {
+ // Arrange
+ InitOptions initOptions = CreateBasicInitOptionsForMsSqlWithConfig(config: TEST_RUNTIME_CONFIG_FILE);
+ Assert.IsTrue(ConfigGenerator.TryGenerateConfig(initOptions, _runtimeConfigLoader!, _fileSystem!));
+
+ // Create first definition
+ AutoentitiesConfigureOptions options1 = new(
+ definitionName: "def-1",
+ patternsInclude: new[] { "dbo.%" },
+ permissions: new[] { "anonymous", "read" },
+ config: TEST_RUNTIME_CONFIG_FILE
+ );
+ Assert.IsTrue(ConfigGenerator.TryConfigureAutoentities(options1, _runtimeConfigLoader!, _fileSystem!));
+
+ // Create second definition
+ AutoentitiesConfigureOptions options2 = new(
+ definitionName: "def-2",
+ patternsInclude: new[] { "sys.%" },
+ permissions: new[] { "authenticated", "*" },
+ config: TEST_RUNTIME_CONFIG_FILE
+ );
+
+ // Act
+ bool success = ConfigGenerator.TryConfigureAutoentities(options2, _runtimeConfigLoader!, _fileSystem!);
+
+ // Assert
+ Assert.IsTrue(success);
+ Assert.IsTrue(_runtimeConfigLoader!.TryLoadConfig(TEST_RUNTIME_CONFIG_FILE, out RuntimeConfig? config));
+ Assert.AreEqual(2, config.Autoentities!.AutoEntities.Count);
+ Assert.IsTrue(config.Autoentities.AutoEntities.ContainsKey("def-1"));
+ Assert.IsTrue(config.Autoentities.AutoEntities.ContainsKey("def-2"));
+ }
+
+ ///
+ /// Tests that attempting to configure autoentities without a config file fails.
+ ///
+ [TestMethod]
+ public void TestConfigureAutoentitiesDefinition_NoConfigFile()
+ {
+ // Arrange
+ AutoentitiesConfigureOptions options = new(
+ definitionName: "test-def",
+ permissions: new[] { "anonymous", "read" }
+ );
+
+ // Act
+ bool success = ConfigGenerator.TryConfigureAutoentities(options, _runtimeConfigLoader!, _fileSystem!);
+
+ // Assert
+ Assert.IsFalse(success);
+ }
+}
diff --git a/src/Cli/Commands/AutoentitiesConfigureOptions.cs b/src/Cli/Commands/AutoentitiesConfigureOptions.cs
new file mode 100644
index 0000000000..bf6b7357e9
--- /dev/null
+++ b/src/Cli/Commands/AutoentitiesConfigureOptions.cs
@@ -0,0 +1,104 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.IO.Abstractions;
+using Azure.DataApiBuilder.Config;
+using Azure.DataApiBuilder.Product;
+using Cli.Constants;
+using CommandLine;
+using Microsoft.Extensions.Logging;
+using static Cli.Utils;
+using ILogger = Microsoft.Extensions.Logging.ILogger;
+
+namespace Cli.Commands
+{
+ ///
+ /// AutoentitiesConfigureOptions command options
+ /// This command will be used to configure autoentities definitions in the config file.
+ ///
+ [Verb("autoentities-configure", isDefault: false, HelpText = "Configure autoentities definitions", Hidden = false)]
+ public class AutoentitiesConfigureOptions : Options
+ {
+ public AutoentitiesConfigureOptions(
+ string definitionName,
+ IEnumerable? patternsInclude = null,
+ IEnumerable? patternsExclude = null,
+ string? patternsName = null,
+ string? templateMcpDmlTool = null,
+ bool? templateRestEnabled = null,
+ bool? templateGraphqlEnabled = null,
+ bool? templateCacheEnabled = null,
+ int? templateCacheTtlSeconds = null,
+ string? templateCacheLevel = null,
+ bool? templateHealthEnabled = null,
+ IEnumerable? permissions = null,
+ string? config = null)
+ : base(config)
+ {
+ DefinitionName = definitionName;
+ PatternsInclude = patternsInclude;
+ PatternsExclude = patternsExclude;
+ PatternsName = patternsName;
+ TemplateMcpDmlTool = templateMcpDmlTool;
+ TemplateRestEnabled = templateRestEnabled;
+ TemplateGraphqlEnabled = templateGraphqlEnabled;
+ TemplateCacheEnabled = templateCacheEnabled;
+ TemplateCacheTtlSeconds = templateCacheTtlSeconds;
+ TemplateCacheLevel = templateCacheLevel;
+ TemplateHealthEnabled = templateHealthEnabled;
+ Permissions = permissions;
+ }
+
+ [Value(0, Required = true, HelpText = "Name of the autoentities definition to configure.")]
+ public string DefinitionName { get; }
+
+ [Option("patterns.include", Required = false, HelpText = "T-SQL LIKE pattern(s) to include database objects. Space-separated array of patterns.")]
+ public IEnumerable? PatternsInclude { get; }
+
+ [Option("patterns.exclude", Required = false, HelpText = "T-SQL LIKE pattern(s) to exclude database objects. Space-separated array of patterns.")]
+ public IEnumerable? PatternsExclude { get; }
+
+ [Option("patterns.name", Required = false, HelpText = "Interpolation syntax for entity naming (must be unique for each generated entity).")]
+ public string? PatternsName { get; }
+
+ [Option("template.mcp.dml-tool", Required = false, HelpText = "Enable/disable DML tools for generated entities. Allowed values: true, false.")]
+ public string? TemplateMcpDmlTool { get; }
+
+ [Option("template.rest.enabled", Required = false, HelpText = "Enable/disable REST endpoint for generated entities. Allowed values: true, false.")]
+ public bool? TemplateRestEnabled { get; }
+
+ [Option("template.graphql.enabled", Required = false, HelpText = "Enable/disable GraphQL endpoint for generated entities. Allowed values: true, false.")]
+ public bool? TemplateGraphqlEnabled { get; }
+
+ [Option("template.cache.enabled", Required = false, HelpText = "Enable/disable cache for generated entities. Allowed values: true, false.")]
+ public bool? TemplateCacheEnabled { get; }
+
+ [Option("template.cache.ttl-seconds", Required = false, HelpText = "Cache time-to-live in seconds for generated entities.")]
+ public int? TemplateCacheTtlSeconds { get; }
+
+ [Option("template.cache.level", Required = false, HelpText = "Cache level for generated entities. Allowed values: L1, L1L2.")]
+ public string? TemplateCacheLevel { get; }
+
+ [Option("template.health.enabled", Required = false, HelpText = "Enable/disable health check for generated entities. Allowed values: true, false.")]
+ public bool? TemplateHealthEnabled { get; }
+
+ [Option("permissions", Required = false, Separator = ':', HelpText = "Permissions for generated entities in the format role:actions (e.g., anonymous:read).")]
+ public IEnumerable? Permissions { get; }
+
+ public int Handler(ILogger logger, FileSystemRuntimeConfigLoader loader, IFileSystem fileSystem)
+ {
+ logger.LogInformation("{productName} {version}", PRODUCT_NAME, ProductInfo.GetProductVersion());
+ bool isSuccess = ConfigGenerator.TryConfigureAutoentities(this, loader, fileSystem);
+ if (isSuccess)
+ {
+ logger.LogInformation("Successfully configured autoentities definition: {DefinitionName}.", DefinitionName);
+ return CliReturnCode.SUCCESS;
+ }
+ else
+ {
+ logger.LogError("Failed to configure autoentities definition: {DefinitionName}.", DefinitionName);
+ return CliReturnCode.GENERAL_ERROR;
+ }
+ }
+ }
+}
diff --git a/src/Cli/ConfigGenerator.cs b/src/Cli/ConfigGenerator.cs
index 78a5e63a7d..1ae9303ab4 100644
--- a/src/Cli/ConfigGenerator.cs
+++ b/src/Cli/ConfigGenerator.cs
@@ -2747,6 +2747,267 @@ public static bool TryAddTelemetry(AddTelemetryOptions options, FileSystemRuntim
return WriteRuntimeConfigToFile(runtimeConfigFile, runtimeConfig, fileSystem);
}
+ ///
+ /// Configures an autoentities definition in the runtime config.
+ /// This method updates or creates an autoentities definition with the specified patterns, template, and permissions.
+ ///
+ /// The autoentities configuration options provided by the user.
+ /// The config loader to read the existing config.
+ /// The filesystem used for reading and writing the config file.
+ /// True if the autoentities definition was successfully configured; otherwise, false.
+ public static bool TryConfigureAutoentities(AutoentitiesConfigureOptions options, FileSystemRuntimeConfigLoader loader, IFileSystem fileSystem)
+ {
+ if (!TryGetConfigFileBasedOnCliPrecedence(loader, options.Config, out string runtimeConfigFile))
+ {
+ return false;
+ }
+
+ if (!loader.TryLoadConfig(runtimeConfigFile, out RuntimeConfig? runtimeConfig))
+ {
+ _logger.LogError("Failed to read the config file: {runtimeConfigFile}.", runtimeConfigFile);
+ return false;
+ }
+
+ // Get existing autoentities or create new collection
+ Dictionary autoEntitiesDictionary = runtimeConfig.Autoentities?.AutoEntities != null
+ ? new Dictionary(runtimeConfig.Autoentities.AutoEntities)
+ : new Dictionary();
+
+ // Get existing autoentity definition or create a new one
+ Autoentity? existingAutoentity = null;
+ if (autoEntitiesDictionary.TryGetValue(options.DefinitionName, out Autoentity? value))
+ {
+ existingAutoentity = value;
+ }
+
+ // Build patterns
+ AutoentityPatterns patterns = BuildAutoentityPatterns(options, existingAutoentity);
+
+ // Build template
+ AutoentityTemplate? template = BuildAutoentityTemplate(options, existingAutoentity);
+ if (template is null)
+ {
+ return false;
+ }
+
+ // Build permissions
+ EntityPermission[]? permissions = BuildAutoentityPermissions(options, existingAutoentity);
+ if (permissions is null && options.Permissions is not null)
+ {
+ _logger.LogError("Failed to parse permissions.");
+ return false;
+ }
+
+ // Create updated autoentity
+ Autoentity updatedAutoentity = new(
+ Patterns: patterns,
+ Template: template,
+ Permissions: permissions ?? (existingAutoentity?.Permissions ?? Array.Empty())
+ );
+
+ // Update the dictionary
+ autoEntitiesDictionary[options.DefinitionName] = updatedAutoentity;
+
+ // Update runtime config
+ runtimeConfig = runtimeConfig with
+ {
+ Autoentities = new RuntimeAutoentities(autoEntitiesDictionary)
+ };
+
+ return WriteRuntimeConfigToFile(runtimeConfigFile, runtimeConfig, fileSystem);
+ }
+
+ ///
+ /// Builds the AutoentityPatterns object from the provided options and existing autoentity.
+ ///
+ private static AutoentityPatterns BuildAutoentityPatterns(AutoentitiesConfigureOptions options, Autoentity? existingAutoentity)
+ {
+ string[]? include = null;
+ string[]? exclude = null;
+ string? name = null;
+ bool userProvidedInclude = false;
+ bool userProvidedExclude = false;
+ bool userProvidedName = false;
+
+ // Start with existing values
+ if (existingAutoentity is not null)
+ {
+ include = existingAutoentity.Patterns.Include;
+ exclude = existingAutoentity.Patterns.Exclude;
+ name = existingAutoentity.Patterns.Name;
+ userProvidedInclude = existingAutoentity.Patterns.UserProvidedIncludeOptions;
+ userProvidedExclude = existingAutoentity.Patterns.UserProvidedExcludeOptions;
+ userProvidedName = existingAutoentity.Patterns.UserProvidedNameOptions;
+ }
+
+ // Override with new values if provided
+ if (options.PatternsInclude is not null && options.PatternsInclude.Any())
+ {
+ include = options.PatternsInclude.ToArray();
+ userProvidedInclude = true;
+ _logger.LogInformation("Updated patterns.include for definition '{DefinitionName}'", options.DefinitionName);
+ }
+
+ if (options.PatternsExclude is not null && options.PatternsExclude.Any())
+ {
+ exclude = options.PatternsExclude.ToArray();
+ userProvidedExclude = true;
+ _logger.LogInformation("Updated patterns.exclude for definition '{DefinitionName}'", options.DefinitionName);
+ }
+
+ if (!string.IsNullOrWhiteSpace(options.PatternsName))
+ {
+ name = options.PatternsName;
+ userProvidedName = true;
+ _logger.LogInformation("Updated patterns.name for definition '{DefinitionName}'", options.DefinitionName);
+ }
+
+ return new AutoentityPatterns(Include: include, Exclude: exclude, Name: name)
+ {
+ UserProvidedIncludeOptions = userProvidedInclude,
+ UserProvidedExcludeOptions = userProvidedExclude,
+ UserProvidedNameOptions = userProvidedName
+ };
+ }
+
+ ///
+ /// Builds the AutoentityTemplate object from the provided options and existing autoentity.
+ /// Returns null if validation fails.
+ ///
+ private static AutoentityTemplate? BuildAutoentityTemplate(AutoentitiesConfigureOptions options, Autoentity? existingAutoentity)
+ {
+ // Start with existing values or defaults
+ EntityMcpOptions? mcp = existingAutoentity?.Template.Mcp;
+ EntityRestOptions rest = existingAutoentity?.Template.Rest ?? new EntityRestOptions();
+ EntityGraphQLOptions graphQL = existingAutoentity?.Template.GraphQL ?? new EntityGraphQLOptions(string.Empty, string.Empty);
+ EntityHealthCheckConfig health = existingAutoentity?.Template.Health ?? new EntityHealthCheckConfig();
+ EntityCacheOptions cache = existingAutoentity?.Template.Cache ?? new EntityCacheOptions();
+
+ bool userProvidedMcp = existingAutoentity?.Template.UserProvidedMcpOptions ?? false;
+ bool userProvidedRest = existingAutoentity?.Template.UserProvidedRestOptions ?? false;
+ bool userProvidedGraphQL = existingAutoentity?.Template.UserProvidedGraphQLOptions ?? false;
+ bool userProvidedHealth = existingAutoentity?.Template.UserProvidedHealthOptions ?? false;
+ bool userProvidedCache = existingAutoentity?.Template.UserProvidedCacheOptions ?? false;
+
+ // Update MCP options
+ if (!string.IsNullOrWhiteSpace(options.TemplateMcpDmlTool))
+ {
+ if (!bool.TryParse(options.TemplateMcpDmlTool, out bool mcpDmlToolValue))
+ {
+ _logger.LogError("Invalid value for template.mcp.dml-tool: {value}. Expected: true or false.", options.TemplateMcpDmlTool);
+ return null;
+ }
+
+ bool? customToolEnabled = mcp?.UserProvidedCustomToolEnabled == true ? mcp.CustomToolEnabled : null;
+ bool? dmlToolValue = mcpDmlToolValue;
+ mcp = new EntityMcpOptions(customToolEnabled: customToolEnabled, dmlToolsEnabled: dmlToolValue);
+ userProvidedMcp = true;
+ _logger.LogInformation("Updated template.mcp.dml-tool for definition '{DefinitionName}'", options.DefinitionName);
+ }
+
+ // Update REST options
+ if (options.TemplateRestEnabled is not null)
+ {
+ rest = rest with { Enabled = options.TemplateRestEnabled.Value };
+ userProvidedRest = true;
+ _logger.LogInformation("Updated template.rest.enabled for definition '{DefinitionName}'", options.DefinitionName);
+ }
+
+ // Update GraphQL options
+ if (options.TemplateGraphqlEnabled is not null)
+ {
+ graphQL = graphQL with { Enabled = options.TemplateGraphqlEnabled.Value };
+ userProvidedGraphQL = true;
+ _logger.LogInformation("Updated template.graphql.enabled for definition '{DefinitionName}'", options.DefinitionName);
+ }
+
+ // Update Health options
+ if (options.TemplateHealthEnabled is not null)
+ {
+ health = new EntityHealthCheckConfig(
+ enabled: options.TemplateHealthEnabled.Value,
+ first: health.UserProvidedFirst ? health.First : null,
+ thresholdMs: health.UserProvidedThresholdMs ? health.ThresholdMs : null
+ );
+ userProvidedHealth = true;
+ _logger.LogInformation("Updated template.health.enabled for definition '{DefinitionName}'", options.DefinitionName);
+ }
+
+ // Update Cache options
+ bool cacheUpdated = false;
+ bool? cacheEnabled = cache.Enabled;
+ int? cacheTtl = cache.UserProvidedTtlOptions ? cache.TtlSeconds : null;
+ EntityCacheLevel? cacheLevel = cache.UserProvidedLevelOptions ? cache.Level : null;
+
+ if (options.TemplateCacheEnabled is not null)
+ {
+ cacheEnabled = options.TemplateCacheEnabled.Value;
+ cacheUpdated = true;
+ _logger.LogInformation("Updated template.cache.enabled for definition '{DefinitionName}'", options.DefinitionName);
+ }
+
+ if (options.TemplateCacheTtlSeconds is not null)
+ {
+ cacheTtl = options.TemplateCacheTtlSeconds.Value;
+ cacheUpdated = true;
+ _logger.LogInformation("Updated template.cache.ttl-seconds for definition '{DefinitionName}'", options.DefinitionName);
+ }
+
+ if (!string.IsNullOrWhiteSpace(options.TemplateCacheLevel))
+ {
+ if (!Enum.TryParse(options.TemplateCacheLevel, ignoreCase: true, out EntityCacheLevel cacheLevelValue))
+ {
+ _logger.LogError("Invalid value for template.cache.level: {value}. Allowed values: L1, L1L2.", options.TemplateCacheLevel);
+ return null;
+ }
+
+ cacheLevel = cacheLevelValue;
+ cacheUpdated = true;
+ _logger.LogInformation("Updated template.cache.level for definition '{DefinitionName}'", options.DefinitionName);
+ }
+
+ if (cacheUpdated)
+ {
+ cache = new EntityCacheOptions(Enabled: cacheEnabled, TtlSeconds: cacheTtl, Level: cacheLevel);
+ userProvidedCache = true;
+ }
+
+ return new AutoentityTemplate(
+ Rest: rest,
+ GraphQL: graphQL,
+ Mcp: mcp,
+ Health: health,
+ Cache: cache
+ )
+ {
+ UserProvidedMcpOptions = userProvidedMcp,
+ UserProvidedRestOptions = userProvidedRest,
+ UserProvidedGraphQLOptions = userProvidedGraphQL,
+ UserProvidedHealthOptions = userProvidedHealth,
+ UserProvidedCacheOptions = userProvidedCache
+ };
+ }
+
+ ///
+ /// Builds the permissions array from the provided options and existing autoentity.
+ ///
+ private static EntityPermission[]? BuildAutoentityPermissions(AutoentitiesConfigureOptions options, Autoentity? existingAutoentity)
+ {
+ if (options.Permissions is null || !options.Permissions.Any())
+ {
+ return existingAutoentity?.Permissions;
+ }
+
+ // Parse the permissions
+ EntityPermission[]? parsedPermissions = ParsePermission(options.Permissions, null, null, null);
+ if (parsedPermissions is not null)
+ {
+ _logger.LogInformation("Updated permissions for definition '{DefinitionName}'", options.DefinitionName);
+ }
+
+ return parsedPermissions;
+ }
+
///
/// Attempts to update the Azure Key Vault configuration options based on the provided values.
/// Validates that any user-provided parameter value is valid and updates the runtime configuration accordingly.
diff --git a/src/Cli/Program.cs b/src/Cli/Program.cs
index 036f3dc2a3..c2662f01e9 100644
--- a/src/Cli/Program.cs
+++ b/src/Cli/Program.cs
@@ -58,7 +58,7 @@ public static int Execute(string[] args, ILogger cliLogger, IFileSystem fileSyst
});
// Parsing user arguments and executing required methods.
- int result = parser.ParseArguments(args)
+ int result = parser.ParseArguments(args)
.MapResult(
(InitOptions options) => options.Handler(cliLogger, loader, fileSystem),
(AddOptions options) => options.Handler(cliLogger, loader, fileSystem),
@@ -67,6 +67,7 @@ public static int Execute(string[] args, ILogger cliLogger, IFileSystem fileSyst
(ValidateOptions options) => options.Handler(cliLogger, loader, fileSystem),
(AddTelemetryOptions options) => options.Handler(cliLogger, loader, fileSystem),
(ConfigureOptions options) => options.Handler(cliLogger, loader, fileSystem),
+ (AutoentitiesConfigureOptions options) => options.Handler(cliLogger, loader, fileSystem),
(ExportOptions options) => options.Handler(cliLogger, loader, fileSystem),
errors => DabCliParserErrorHandler.ProcessErrorsAndReturnExitCode(errors));
diff --git a/src/Config/Converters/AutoentityConverter.cs b/src/Config/Converters/AutoentityConverter.cs
index 5c09ed8e7b..47b6414ae4 100644
--- a/src/Config/Converters/AutoentityConverter.cs
+++ b/src/Config/Converters/AutoentityConverter.cs
@@ -90,6 +90,7 @@ public override void Write(Utf8JsonWriter writer, Autoentity value, JsonSerializ
AutoentityTemplate? template = value?.Template;
if (template?.UserProvidedRestOptions is true
|| template?.UserProvidedGraphQLOptions is true
+ || template?.UserProvidedMcpOptions is true
|| template?.UserProvidedHealthOptions is true
|| template?.UserProvidedCacheOptions is true)
{
diff --git a/src/Config/Converters/AutoentityTemplateConverter.cs b/src/Config/Converters/AutoentityTemplateConverter.cs
index 275cfc4314..2f5ea3407f 100644
--- a/src/Config/Converters/AutoentityTemplateConverter.cs
+++ b/src/Config/Converters/AutoentityTemplateConverter.cs
@@ -116,6 +116,12 @@ public override void Write(Utf8JsonWriter writer, AutoentityTemplate value, Json
JsonSerializer.Serialize(writer, value.GraphQL, options);
}
+ if (value?.UserProvidedMcpOptions is true)
+ {
+ writer.WritePropertyName("mcp");
+ JsonSerializer.Serialize(writer, value.Mcp, options);
+ }
+
if (value?.UserProvidedHealthOptions is true)
{
writer.WritePropertyName("health");