Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
81330ac
Initial plan
Copilot Nov 13, 2025
7cfbd5d
Add autoentities schema and C# models for wildcard properties
Copilot Nov 13, 2025
15e4674
Add test for autoentities serialization/deserialization
Copilot Nov 13, 2025
92fe8a2
Add test config file with autoentities
Copilot Nov 13, 2025
e02c53d
Changes to schema file
RubenCerna2079 Nov 17, 2025
6cbba87
Fixes to schema file
RubenCerna2079 Nov 18, 2025
8c25493
Add serialization and deserialization of autoentities
RubenCerna2079 Nov 21, 2025
fd4df81
Merge branch 'main' into copilot/add-wildcard-properties-schema
RubenCerna2079 Nov 21, 2025
db3d137
Fix merge conflicts
RubenCerna2079 Nov 21, 2025
a443ae0
Fix tests
RubenCerna2079 Nov 24, 2025
853e356
Rename RuntimeAutoEntitiesConverter.cs to RuntimeAutoentitiesConverte…
RubenCerna2079 Nov 24, 2025
30ac9d6
Rename AutoEntity.cs to Autoentity.cs
RubenCerna2079 Nov 24, 2025
48c48f4
Rename RuntimeAutoEntities.cs to RuntimeAutoentities.cs
RubenCerna2079 Nov 24, 2025
1dcfbed
Rename AutoEntityPatterns.cs to AutoentityPatterns.cs
RubenCerna2079 Nov 24, 2025
7be3a41
Rename AutoEntityTemplate.cs to AutoentityTemplate.cs
RubenCerna2079 Nov 24, 2025
71dfe30
Merge branch 'main' into copilot/add-wildcard-properties-schema
RubenCerna2079 Nov 26, 2025
5ce4107
Changes based on comments
RubenCerna2079 Nov 26, 2025
0d97798
Changes based on comments
RubenCerna2079 Nov 26, 2025
8cfde2e
Changes based on comments
RubenCerna2079 Nov 26, 2025
ed786ce
Merge branch 'main' into copilot/add-wildcard-properties-schema
RubenCerna2079 Nov 26, 2025
3a860f1
Fix tests
RubenCerna2079 Dec 4, 2025
014808e
Fix failing tests
RubenCerna2079 Dec 4, 2025
d399911
Merge branch 'main' into copilot/add-wildcard-properties-schema
RubenCerna2079 Dec 4, 2025
fcfabef
Fix entity permission
RubenCerna2079 Dec 4, 2025
e731411
Fix schema
RubenCerna2079 Dec 5, 2025
0b4ea58
Merge branch 'main' into copilot/add-wildcard-properties-schema
RubenCerna2079 Dec 5, 2025
8fc87af
Fix formatting issues
RubenCerna2079 Dec 5, 2025
1f840bd
Changes based on comments
RubenCerna2079 Dec 19, 2025
1247a48
Merge branch 'main' into copilot/add-wildcard-properties-schema
RubenCerna2079 Dec 19, 2025
6f8178a
Add missing mcp entity property
RubenCerna2079 Dec 19, 2025
4cef2ea
Merge branch 'main' into copilot/add-wildcard-properties-schema
RubenCerna2079 Dec 22, 2025
80ff055
Add additionalProperties to schema
RubenCerna2079 Dec 22, 2025
f3cd932
Merge branch 'main' into copilot/add-wildcard-properties-schema
RubenCerna2079 Dec 22, 2025
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
160 changes: 160 additions & 0 deletions schemas/dab.draft.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,166 @@
}
}
},
"autoentities": {
"type": "object",
"description": "Defines automatic entity generation rules for MSSQL tables based on include/exclude patterns and defaults.",
"patternProperties": {
"^.*$": {
"type": "object",
"additionalProperties": false,
"properties": {
"patterns": {
"type": "object",
"description": "Pattern matching rules for including/excluding database objects",
"additionalProperties": false,
"properties": {
"include": {
"type": "array",
"description": "MSSQL LIKE pattern for objects to include (e.g., '%.%'). Null includes all.",
"items": {
"type": "string"
},
"default": [ "%.%" ]
},
"exclude": {
"type": "array",
"description": "MSSQL LIKE pattern for objects to exclude (e.g., 'sales.%'). Null excludes none.",
"items": {
"type": "string"
},
"default": null
},
"name": {
"type": "string",
"description": "Entity name interpolation pattern using {schema} and {object}. Null defaults to {object}. Must be unique for every entity inside the pattern",
"default": "{object}"
}
}
},
"template": {
"type": "object",
"description": "Template configuration for generated entities",
"additionalProperties": false,
"properties": {
"mcp": {
"type": "object",
"description": "MCP endpoint configuration",
"additionalProperties": false,
"properties": {
"dml-tools": {
"type": "boolean",
"description": "Enable/disable all DML tools with default settings."
}
}
},
"rest": {
"type": "object",
"description": "REST endpoint configuration",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable/disable REST endpoint",
"default": true
}
}
},
"graphql": {
"type": "object",
"description": "GraphQL endpoint configuration",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable/disable GraphQL endpoint",
"default": true
}
}
},
"health": {
"type": "object",
"description": "Health check configuration",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable/disable health check endpoint",
"default": true
}
}
},
"cache": {
"type": "object",
"description": "Cache configuration",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable/disable caching",
"default": false
},
"ttl-seconds": {
"type": [ "integer", "null" ],
"description": "Time-to-live for cached responses in seconds",
"default": null,
"minimum": 1
},
"level": {
"type": "string",
"description": "Cache level (L1 or L1L2)",
"enum": [ "L1", "L1L2", null ],
"default": "L1L2"
}
}
}
}
},
"permissions": {
"type": "array",
"description": "Permissions assigned to this object",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"role": {
"type": "string"
},
"actions": {
"oneOf": [
{
"type": "string",
"pattern": "[*]"
},
{
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/$defs/action"
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"action": {
"$ref": "#/$defs/action"
}
}
}
]
},
"uniqueItems": true
}
]
}
}
},
"required": [ "role", "actions" ]
}
}
}
}
},
"entities": {
"type": "object",
"description": "Entities that will be exposed via REST, GraphQL and/or MCP",
Expand Down
110 changes: 110 additions & 0 deletions src/Config/Converters/AutoentityConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json;
using System.Text.Json.Serialization;
using Azure.DataApiBuilder.Config.ObjectModel;

namespace Azure.DataApiBuilder.Config.Converters;

internal class AutoentityConverter : JsonConverter<Autoentity>
{
// Settings for variable replacement during deserialization.
private readonly DeserializationVariableReplacementSettings? _replacementSettings;

/// <param name="replacementSettings">Settings for variable replacement during deserialization.
/// If null, no variable replacement will be performed.</param>
public AutoentityConverter(DeserializationVariableReplacementSettings? replacementSettings = null)
{
_replacementSettings = replacementSettings;
}

/// <inheritdoc/>
public override Autoentity? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType is JsonTokenType.StartObject)
{
// Initialize all sub-properties to null.
AutoentityPatterns? patterns = null;
AutoentityTemplate? template = null;
EntityPermission[]? permissions = null;

while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return new Autoentity(patterns, template, permissions);
}

string? propertyName = reader.GetString();

reader.Read();
switch (propertyName)
{
case "patterns":
AutoentityPatternsConverter patternsConverter = new(_replacementSettings);
patterns = patternsConverter.Read(ref reader, typeof(AutoentityPatterns), options);
break;

case "template":
AutoentityTemplateConverter templateConverter = new(_replacementSettings);
template = templateConverter.Read(ref reader, typeof(AutoentityTemplate), options);
break;

case "permissions":
permissions = JsonSerializer.Deserialize<EntityPermission[]>(ref reader, options)
?? throw new JsonException("The 'permissions' property must contain at least one permission.");
break;

default:
throw new JsonException($"Unexpected property {propertyName}");
}
}
}

throw new JsonException("Failed to read the Autoentities");
}

/// <summary>
/// When writing the autoentities back to a JSON file, only write the properties
/// if they are user provided. This avoids polluting the written JSON file with properties
/// the user most likely omitted when writing the original DAB runtime config file.
/// This Write operation is only used when a RuntimeConfig object is serialized to JSON.
/// </summary>
/// <inheritdoc/>
public override void Write(Utf8JsonWriter writer, Autoentity value, JsonSerializerOptions options)
{
writer.WriteStartObject();

AutoentityPatterns? patterns = value?.Patterns;
if (patterns?.UserProvidedIncludeOptions is true
|| patterns?.UserProvidedExcludeOptions is true
|| patterns?.UserProvidedNameOptions is true)
{
AutoentityPatternsConverter autoentityPatternsConverter = options.GetConverter(typeof(AutoentityPatterns)) as AutoentityPatternsConverter ??
throw new JsonException("Failed to get autoentities.patterns options converter");
writer.WritePropertyName("patterns");
autoentityPatternsConverter.Write(writer, patterns, options);
}

AutoentityTemplate? template = value?.Template;
if (template?.UserProvidedRestOptions is true
|| template?.UserProvidedGraphQLOptions is true
|| template?.UserProvidedHealthOptions is true
|| template?.UserProvidedCacheOptions is true)
{
AutoentityTemplateConverter autoentityTemplateConverter = options.GetConverter(typeof(AutoentityTemplate)) as AutoentityTemplateConverter ??
throw new JsonException("Failed to get autoentities.template options converter");
writer.WritePropertyName("template");
autoentityTemplateConverter.Write(writer, template, options);
}

if (value?.Permissions is not null)
{
writer.WritePropertyName("permissions");
JsonSerializer.Serialize(writer, value.Permissions, options);
}

writer.WriteEndObject();
}
}
Loading
Loading