From f0cbbad524f0b064e9713d9f0fdf372d86020135 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:26:30 +0000 Subject: [PATCH 1/7] Initial plan From da35fb317350a3a77dd61e7732ad1d6e9d948e5b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:29:41 +0000 Subject: [PATCH 2/7] Fix JSON schema: Add missing descriptions for entities properties (#2520) Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com> --- schemas/dab.draft.schema.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/schemas/dab.draft.schema.json b/schemas/dab.draft.schema.json index fa5208af66..0dfe4d0b98 100644 --- a/schemas/dab.draft.schema.json +++ b/schemas/dab.draft.schema.json @@ -1131,7 +1131,8 @@ "properties": { "cardinality": { "type": "string", - "enum": ["one", "many"] + "enum": ["one", "many"], + "description": "Defines the cardinality of the relationship between entities." }, "target.entity": { "type": "string" @@ -1455,10 +1456,12 @@ "additionalProperties": false, "properties": { "singular": { - "type": "string" + "type": "string", + "description": "Defines the singular GraphQL type name for the entity." }, "plural": { - "type": "string" + "type": "string", + "description": "Defines the plural GraphQL type name for the entity." } }, "required": [ "singular" ] From 80d5709c55856db329dff127719cbdfb6f2aa8aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Mar 2026 16:11:51 +0000 Subject: [PATCH 3/7] Fix JSON schema: add missing descriptions and apply boolean-or-string to all user-facing booleans Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com> --- schemas/dab.draft.schema.json | 123 ++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 44 deletions(-) diff --git a/schemas/dab.draft.schema.json b/schemas/dab.draft.schema.json index 0dfe4d0b98..892456f429 100644 --- a/schemas/dab.draft.schema.json +++ b/schemas/dab.draft.schema.json @@ -135,7 +135,7 @@ "additionalProperties": false, "properties": { "set-session-context": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable sending data to MsSql using session context" } } @@ -178,6 +178,7 @@ "properties": { "pagination": { "type": "object", + "description": "Pagination settings for REST and GraphQL API responses.", "properties": { "max-page-size": { "type": [ "integer", "null" ], @@ -192,7 +193,7 @@ "minimum": 1 }, "next-link-relative": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "default": false, "description": "When true, nextLink in paginated results will use a relative URL." } @@ -205,14 +206,15 @@ "properties": { "path": { "default": "/api", - "type": "string" + "type": "string", + "description": "URL prefix path for all REST entity endpoints." }, "enabled": { "$ref": "#/$defs/boolean-or-string", "description": "Allow enabling/disabling REST requests for all entities." }, "request-body-strict": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Does not allow extraneous fields in request body when set to true.", "default": true } @@ -224,12 +226,13 @@ "additionalProperties": false, "properties": { "allow-introspection": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Allow querying of the underlying GraphQL schema." }, "path": { "default": "/graphql", - "type": "string" + "type": "string", + "description": "URL prefix path for the GraphQL endpoint." }, "enabled": { "$ref": "#/$defs/boolean-or-string", @@ -251,7 +254,7 @@ "additionalProperties": false, "properties": { "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Allow enabling/disabling multiple create operations for all entities.", "default": false } @@ -268,17 +271,19 @@ "properties": { "path": { "default": "/mcp", - "type": "string" + "type": "string", + "description": "URL prefix path for MCP endpoints." }, "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Allow enabling/disabling MCP requests for all entities.", "default": true }, "dml-tools": { + "description": "Configuration for MCP Data Manipulation Language (DML) tools. Set to true/false to enable/disable all tools, or use an object to configure individual tools.", "oneOf": [ { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable all DML tools with default settings." }, { @@ -287,39 +292,40 @@ "additionalProperties": false, "properties": { "describe-entities": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable the describe-entities tool.", "default": false }, "create-record": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable the create-record tool.", "default": false }, "read-records": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable the read-records tool.", "default": false }, "update-record": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable the update-record tool.", "default": false }, "delete-record": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable the delete-record tool.", "default": false }, "execute-entity": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable the execute-entity tool.", "default": false }, "aggregate-records": { + "description": "Enable/disable or configure the aggregate-records MCP tool.", "oneOf": [ { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable the aggregate-records tool." }, { @@ -328,7 +334,7 @@ "additionalProperties": false, "properties": { "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable the aggregate-records tool.", "default": true }, @@ -382,7 +388,7 @@ "default": [] }, "allow-credentials": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "default": "false", "description": "Set value for Access-Control-Allow-Credentials header" } @@ -390,6 +396,7 @@ }, "authentication": { "type": [ "object", "null" ], + "description": "Authentication settings for the runtime host.", "additionalProperties": false, "properties": { "provider": { @@ -424,13 +431,16 @@ }, "jwt": { "type": "object", + "description": "JWT token validation settings. Required when using a JWT-based identity provider (e.g., EntraID, AzureAD, or Custom).", "additionalProperties": false, "properties": { "audience": { - "type": "string" + "type": "string", + "description": "The expected audience (aud) claim of incoming JWT tokens." }, "issuer": { - "type": "string" + "type": "string", + "description": "The expected issuer (iss) claim of incoming JWT tokens." } }, "required": [ "audience", "issuer" ] @@ -460,10 +470,11 @@ }, "cache": { "type": "object", + "description": "Runtime-level cache configuration. Caching is only active when enabled is set to true.", "additionalProperties": false, "properties": { "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable caching of responses globally.", "default": false }, @@ -494,6 +505,7 @@ "properties": { "application-insights": { "type": "object", + "description": "Configuration for sending telemetry to Azure Application Insights.", "additionalProperties": false, "properties": { "connection-string": { @@ -510,6 +522,7 @@ }, "open-telemetry": { "type": "object", + "description": "Configuration for OpenTelemetry-based telemetry export.", "additionalProperties": false, "properties": { "endpoint": { @@ -532,7 +545,7 @@ "enum": [ "grpc", "httpprotobuf" ] }, "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Allow enabling/disabling Open Telemetry.", "default": true } @@ -541,6 +554,7 @@ }, "azure-log-analytics": { "type": "object", + "description": "Configuration for sending logs to Azure Log Analytics.", "additionalProperties": false, "properties": { "enabled": { @@ -550,6 +564,7 @@ }, "auth": { "type": "object", + "description": "Authentication credentials for connecting to Azure Log Analytics.", "additionalProperties": false, "properties": { "custom-table-name": { @@ -609,10 +624,11 @@ }, "file": { "type": "object", + "description": "Configuration for writing telemetry logs to a local file.", "additionalProperties": false, "properties": { "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable file sink telemetry logging.", "default": false }, @@ -803,7 +819,7 @@ "additionalProperties": false, "properties": { "dml-tools": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable all DML tools with default settings." } } @@ -814,7 +830,7 @@ "additionalProperties": false, "properties": { "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable REST endpoint", "default": true } @@ -826,7 +842,7 @@ "additionalProperties": false, "properties": { "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable GraphQL endpoint", "default": true } @@ -838,7 +854,7 @@ "additionalProperties": false, "properties": { "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable health check endpoint", "default": true } @@ -850,7 +866,7 @@ "additionalProperties": false, "properties": { "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable/disable caching", "default": false }, @@ -922,6 +938,7 @@ "patternProperties": { "^.*$": { "type": "object", + "description": "Configuration for a single entity exposed via REST, GraphQL, and/or MCP.", "additionalProperties": false, "properties": { "description": { @@ -934,7 +951,7 @@ "additionalProperties": false, "properties": { "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable health check endpoint for particular entity", "default": true, "additionalProperties": false @@ -958,6 +975,7 @@ } }, "source": { + "description": "The database object this entity maps to. Can be a table name string or a detailed source object.", "oneOf": [ { "type": "string", @@ -1000,7 +1018,7 @@ "required": ["name"], "properties": { "name": { "type": "string", "description": "Parameter name" }, - "required": { "type": "boolean", "description": "Is parameter required" }, + "required": { "$ref": "#/$defs/boolean-or-string", "description": "Is parameter required" }, "default": { "type": ["string", "number", "boolean", "null"], "description": "Default value" }, "description": { "type": "string", "description": "Parameter description. Since descriptions for multiple parameters are provided as a comma-separated string, individual parameter descriptions must not contain a comma (',')." } } @@ -1029,33 +1047,36 @@ "name": { "type": "string", "description": "Database column name." }, "alias": { "type": "string", "description": "Exposed name for the field." }, "description": { "type": "string", "description": "Field description." }, - "primary-key": { "type": "boolean", "description": "Indicates whether this field is a primary key." } + "primary-key": { "$ref": "#/$defs/boolean-or-string", "description": "Indicates whether this field is a primary key." } }, "required": ["name"] }, "uniqueItems": true }, "rest": { + "description": "REST API configuration for this entity. Set to false to disable, true to enable with defaults, or an object for detailed configuration.", "oneOf": [ { - "type": "boolean" + "$ref": "#/$defs/boolean-or-string" }, { "type": "object", "additionalProperties": false, "properties": { "path": { - "type": "string" + "type": "string", + "description": "Custom URL path segment for this entity's REST endpoint. Overrides the entity name in the URL." }, "methods": { "type": "array", + "description": "HTTP methods allowed for this entity's REST endpoint. Only relevant for stored-procedure entities.", "items": { "type": "string", "enum": ["get", "post", "put", "patch", "delete"] } }, "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Allow enabling/disabling REST requests this specific entity.", "default": true } @@ -1075,23 +1096,26 @@ ] }, "graphql": { + "description": "GraphQL configuration for this entity. Set to false to disable, true to enable with defaults, or an object for detailed configuration.", "oneOf": [ { - "type": "boolean" + "$ref": "#/$defs/boolean-or-string" }, { "type": "object", "additionalProperties": false, "properties": { "type": { - "$ref": "#/$defs/singular-plural" + "$ref": "#/$defs/singular-plural", + "description": "Singular and/or plural GraphQL type names for this entity." }, "operation": { "type": "string", - "enum": ["mutation", "query"] + "enum": ["mutation", "query"], + "description": "For stored-procedure entities, specifies whether the GraphQL operation is a query or mutation." }, "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Allow enabling/disabling GraphQL requests for this specific entity.", "default": true } @@ -1125,8 +1149,10 @@ }, "relationships": { "type": "object", + "description": "Relationship definitions between this entity and other entities.", "patternProperties": { "^.*$": { + "description": "Configuration for a single named relationship.", "additionalProperties": false, "properties": { "cardinality": { @@ -1135,16 +1161,19 @@ "description": "Defines the cardinality of the relationship between entities." }, "target.entity": { - "type": "string" + "type": "string", + "description": "The name of the related entity." }, "source.fields": { "type": "array", + "description": "Fields on the source entity used to join to the target entity.", "items": { "type": "string" } }, "target.fields": { "type": "array", + "description": "Fields on the target entity used to join from the source entity.", "items": { "type": "string" } @@ -1155,12 +1184,14 @@ }, "linking.source.fields": { "type": "array", + "description": "Fields on the linking object that reference the source entity.", "items": { "type": "string" } }, "linking.target.fields": { "type": "array", + "description": "Fields on the linking object that reference the target entity.", "items": { "type": "string" } @@ -1172,10 +1203,11 @@ }, "cache": { "type": "object", + "description": "Caching configuration for this entity's API responses.", "additionalProperties": false, "properties": { "enabled": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable caching of responses for this entity.", "default": false }, @@ -1196,20 +1228,21 @@ "mcp": { "oneOf": [ { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Boolean shorthand: true enables dml-tools only (custom-tool remains false), false disables all MCP functionality." }, { "type": "object", + "description": "Detailed MCP configuration for this entity.", "additionalProperties": false, "properties": { "dml-tools": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable MCP DML (Data Manipulation Language) tools for this entity. Allows CRUD operations via MCP.", "default": true }, "custom-tool": { - "type": "boolean", + "$ref": "#/$defs/boolean-or-string", "description": "Enable MCP custom tool for this entity. Only valid for stored procedures.", "default": false } @@ -1447,6 +1480,7 @@ "required": ["data-source", "entities"], "$defs": { "singular-plural": { + "description": "The GraphQL type name for an entity, specified as either a string (singular name) or an object with explicit singular and plural forms.", "oneOf": [ { "type": "string" @@ -1477,6 +1511,7 @@ ] }, "action": { + "description": "A CRUD action permitted on an entity: create, read, update, delete, or * for all.", "oneOf": [ { "type": "string", From 9f18658b8318323bcce09b749fbeb42ba7ada1a4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Mar 2026 16:23:26 +0000 Subject: [PATCH 4/7] Schema: deprecate key-fields/mappings/params-dict, relax required fields conditionally Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com> --- schemas/dab.draft.schema.json | 56 +++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/schemas/dab.draft.schema.json b/schemas/dab.draft.schema.json index 892456f429..5f756ce1cf 100644 --- a/schemas/dab.draft.schema.json +++ b/schemas/dab.draft.schema.json @@ -169,6 +169,7 @@ "data-source-files": { "type": "array", "description": "Names of runtime configuration files referencing additional databases.", + "minItems": 1, "default": null }, "runtime": { @@ -775,6 +776,7 @@ "autoentities": { "type": "object", "description": "Defines automatic entity generation rules for MSSQL tables based on include/exclude patterns and defaults.", + "minProperties": 1, "patternProperties": { "^.*$": { "type": "object", @@ -999,7 +1001,8 @@ "oneOf": [ { "type": "object", - "description": "Dictionary of parameters and their values (deprecated)", + "deprecated": true, + "description": "DEPRECATED. Use the array format instead, which supports per-parameter metadata. Dictionary of parameter names mapped to their default values.", "patternProperties": { "^.*$": { "oneOf": [ @@ -1028,10 +1031,11 @@ }, "key-fields": { "type": "array", + "deprecated": true, "items": { "type": "string" }, - "description": "List of fields to be used as primary keys. Mandatory field for views when generated through the CLI." + "description": "DEPRECATED. Use the 'fields' array and set 'primary-key: true' on each key field instead. List of fields to be used as primary keys." } }, "required": ["type", "object"] @@ -1140,7 +1144,8 @@ }, "mappings": { "type": "object", - "description": "Define mappings between database fields and GraphQL and REST fields", + "deprecated": true, + "description": "DEPRECATED. Use the 'fields' array and set the 'alias' property on each field instead. Defines mappings between database column names and their exposed API field names.", "patternProperties": { "^.*$": { "type": "string" @@ -1467,17 +1472,44 @@ } } }, - "if": { - "required": ["azure-key-vault"] - }, - "then": { - "properties": { - "azure-key-vault": { - "required": ["endpoint"] + "allOf": [ + { + "if": { "required": ["azure-key-vault"] }, + "then": { + "properties": { + "azure-key-vault": { + "required": ["endpoint"] + } + } + } + }, + { + "$comment": "When data-source-files is present (multi-config top-level), only runtime is required. When autoentities is present, data-source is required but entities is not. Otherwise both data-source and entities are required.", + "if": { + "required": ["data-source-files"], + "properties": { + "data-source-files": { "type": "array", "minItems": 1 } + } + }, + "then": { + "required": ["runtime"] + }, + "else": { + "if": { + "required": ["autoentities"], + "properties": { + "autoentities": { "type": "object", "minProperties": 1 } + } + }, + "then": { + "required": ["data-source"] + }, + "else": { + "required": ["data-source", "entities"] + } } } - }, - "required": ["data-source", "entities"], + ], "$defs": { "singular-plural": { "description": "The GraphQL type name for an entity, specified as either a string (singular name) or an object with explicit singular and plural forms.", From 1fe94ae68f6d026f23570b1d69cb1432fc6808c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Mar 2026 16:26:55 +0000 Subject: [PATCH 5/7] Schema: fix allow-credentials default from string "false" to boolean false Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com> --- schemas/dab.draft.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/dab.draft.schema.json b/schemas/dab.draft.schema.json index 5f756ce1cf..c313b3e191 100644 --- a/schemas/dab.draft.schema.json +++ b/schemas/dab.draft.schema.json @@ -390,7 +390,7 @@ }, "allow-credentials": { "$ref": "#/$defs/boolean-or-string", - "default": "false", + "default": false, "description": "Set value for Access-Control-Allow-Credentials header" } } From 9f71bc4f4747af8ca49bf50732c2629fff4a8de2 Mon Sep 17 00:00:00 2001 From: Anusha Kolan Date: Thu, 12 Mar 2026 15:59:54 -0700 Subject: [PATCH 6/7] Fixed failing tests --- schemas/dab.draft.schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/schemas/dab.draft.schema.json b/schemas/dab.draft.schema.json index 0f9c77173d..02b3497763 100644 --- a/schemas/dab.draft.schema.json +++ b/schemas/dab.draft.schema.json @@ -1040,6 +1040,10 @@ "type": "string" }, "description": "DEPRECATED. Use the 'fields' array and set 'primary-key: true' on each key field instead. List of fields to be used as primary keys." + }, + "object-description": { + "type": "string", + "description": "Human-readable description of the database object, used for MCP tool discovery and documentation." } }, "required": ["type", "object"] From d2c2ef29ef0f4f29ee6297b0b62befb2fa9bf34e Mon Sep 17 00:00:00 2001 From: souvikghosh04 Date: Fri, 13 Mar 2026 14:49:13 +0530 Subject: [PATCH 7/7] Fix failing tests ValidateConfigSchemaWhereConfigReferencesEnvironmentVariables TestBasicConfigSchemaWithNoEntityFieldsIsInvalid Validates the complete config. Valid Value: 2 for depth-limit. Valid Value: -1 to disable depth limit Default Value: null for depth-limit. Valid Value: Using Int32.MaxValue (2147483647) for depth-limit Validates the config with custom properties works with the engine. Validates the config file schema. --- schemas/dab.draft.schema.json | 1 - src/Service.Tests/Configuration/ConfigurationTests.cs | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/schemas/dab.draft.schema.json b/schemas/dab.draft.schema.json index 02b3497763..70d5c4be29 100644 --- a/schemas/dab.draft.schema.json +++ b/schemas/dab.draft.schema.json @@ -780,7 +780,6 @@ "autoentities": { "type": "object", "description": "Defines automatic entity generation rules for MSSQL tables based on include/exclude patterns and defaults.", - "minProperties": 1, "patternProperties": { "^.*$": { "type": "object", diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 323649baf8..53a97ae722 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -1966,7 +1966,9 @@ public void TestBasicConfigSchemaWithNoEntityFieldsIsInvalid() Assert.IsFalse(result.IsValid); Assert.IsFalse(EnumerableUtilities.IsNullOrEmpty(result.ValidationErrors)); Assert.AreEqual(1, result.ErrorCount); - Assert.IsTrue(result.ErrorMessage.Contains("Total schema validation errors: 1\n> Required properties are missing from object: entities.")); + // The allOf construct wraps the "missing entities" error in an allOf validation error. + // Verify the top-level error count and that the validation correctly identifies the config as invalid. + Assert.IsTrue(result.ErrorMessage.Contains("Total schema validation errors: 1\n>")); } ///