Skip to content

Commit 029d51b

Browse files
authored
Merge branch 'main' into copilot/fix-filter-clause-issue
2 parents 7a8f2aa + b11ab1a commit 029d51b

26 files changed

Lines changed: 1614 additions & 131 deletions

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@
1515
*.verified.txt text eol=lf working-tree-encoding=UTF-8
1616
*.verified.xml text eol=lf working-tree-encoding=UTF-8
1717
*.verified.json text eol=lf working-tree-encoding=UTF-8
18+
19+
# This can't be enabled as there are tests that rely on the CRLF endings in files'
20+
#*.cs text eol=lf

.pipelines/mssql-pipelines.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ jobs:
122122
displayName: 'Run MsSql Integration Tests'
123123
inputs:
124124
command: test
125-
arguments: '--filter "TestCategory=MsSql" --no-build --configuration $(buildConfiguration) --collect "XPlat Code coverage"'
125+
arguments: '--filter "TestCategory=MsSql&FullyQualifiedName!~ConfigurationHotReloadTests" --no-build --configuration $(buildConfiguration) --collect "XPlat Code coverage"'
126126
projects: '**/*Tests/*.csproj'
127127

128128
- task: CmdLine@2
@@ -246,11 +246,20 @@ jobs:
246246
targetFiles: 'src/out/tests/*/dab-config.MsSql.json'
247247

248248
- task: DotNetCoreCLI@2
249-
displayName: 'Run MsSql Integration Tests'
249+
displayName: 'MsSql Integration Tests'
250+
inputs:
251+
command: test
252+
arguments: '--filter "TestCategory=MsSql&FullyQualifiedName!~ConfigurationHotReloadTests" --no-build --configuration $(buildConfiguration) --collect "XPlat Code coverage"'
253+
projects: '**/*Tests/*.csproj'
254+
255+
256+
- task: DotNetCoreCLI@2
257+
displayName: 'Hot-Reload Tests'
250258
inputs:
251259
command: test
252-
arguments: '--filter "TestCategory=MsSql" --no-build --configuration $(buildConfiguration) --collect "XPlat Code coverage"'
260+
arguments: '--filter "TestCategory=MsSql&FullyQualifiedName~ConfigurationHotReloadTests" --no-build --configuration $(buildConfiguration) --collect "XPlat Code coverage" --logger "console;verbosity=detailed"'
253261
projects: '**/*Tests/*.csproj'
262+
timeoutInMinutes: 45
254263

255264
- task: PublishCodeCoverageResults@1
256265
displayName: 'Publish code coverage'

CONTRIBUTING.md

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ We use `dotnet format` to enforce code conventions. It is run automatically in C
9595

9696
#### Enforcing code style with git hooks
9797

98-
You can copy paste the following commands to install a git pre-commit hook. This will cause a commit to fail if you forgot to run `dotnet format`. If you have run on save enabled in your editor this is not necessary.
98+
You can copy paste the following commands to install a git pre-commit hook (creates a pre-commit file in your .git folder, which isn't shown in vs code). This will cause a commit to fail if you forgot to run `dotnet format`. If you have run on save enabled in your editor this is not necessary.
9999

100100
```bash
101101
cat > .git/hooks/pre-commit << __EOF__
@@ -112,17 +112,42 @@ if [ "\$(get_files)" = '' ]; then
112112
fi
113113
114114
get_files |
115-
xargs dotnet format src/Azure.DataApiBuilder.Service.sln \\
116-
--check \\
117-
--fix-whitespace --fix-style warn --fix-analyzers warn \\
115+
xargs dotnet format src/Azure.DataApiBuilder.sln \\
116+
--verify-no-changes
118117
--include \\
119118
|| {
120119
get_files |
121-
xargs dotnet format src/Azure.DataApiBuilder.Service.sln \\
122-
--fix-whitespace --fix-style warn --fix-analyzers warn \\
120+
xargs dotnet format src/Azure.DataApiBuilder.sln \\
123121
--include
124122
exit 1
125123
}
126124
__EOF__
127125
chmod +x .git/hooks/pre-commit
128126
```
127+
128+
The file should look like this
129+
130+
``` bash
131+
#!/bin/bash
132+
set -euo pipefail
133+
134+
get_files() {
135+
git diff --cached --name-only --diff-filter=ACMR | \
136+
grep '\.cs$'
137+
}
138+
139+
if [ "$(get_files)" = '' ]; then
140+
exit 0
141+
fi
142+
143+
get_files |
144+
xargs dotnet format src/Azure.DataApiBuilder.sln \
145+
--verify-no-changes \
146+
--include \
147+
|| {
148+
get_files |
149+
xargs dotnet format src/Azure.DataApiBuilder.sln \
150+
--include
151+
exit 1
152+
}
153+
```

schemas/dab.draft.schema.json

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
"additionalProperties": false,
4242
"properties": {
4343
"enabled": {
44-
"type": "boolean",
45-
"description": "Enable health check endpoint",
44+
"$ref": "#/$defs/boolean-or-string",
45+
"description": "Enable health check endpoint for something",
4646
"default": true,
4747
"additionalProperties": false
4848
},
@@ -186,7 +186,7 @@
186186
"type": "string"
187187
},
188188
"enabled": {
189-
"type": "boolean",
189+
"$ref": "#/$defs/boolean-or-string",
190190
"description": "Allow enabling/disabling REST requests for all entities."
191191
},
192192
"request-body-strict": {
@@ -210,7 +210,7 @@
210210
"type": "string"
211211
},
212212
"enabled": {
213-
"type": "boolean",
213+
"$ref": "#/$defs/boolean-or-string",
214214
"description": "Allow enabling/disabling GraphQL requests for all entities."
215215
},
216216
"depth-limit": {
@@ -438,7 +438,7 @@
438438
"description": "Application Insights connection string"
439439
},
440440
"enabled": {
441-
"type": "boolean",
441+
"$ref": "#/$defs/boolean-or-string",
442442
"description": "Allow enabling/disabling Application Insights telemetry.",
443443
"default": true
444444
}
@@ -481,7 +481,7 @@
481481
"additionalProperties": false,
482482
"properties": {
483483
"enabled": {
484-
"type": "boolean",
484+
"$ref": "#/$defs/boolean-or-string",
485485
"description": "Allow enabling/disabling Azure Log Analytics.",
486486
"default": false
487487
},
@@ -618,7 +618,7 @@
618618
"additionalProperties": false,
619619
"properties": {
620620
"enabled": {
621-
"type": "boolean",
621+
"$ref": "#/$defs/boolean-or-string",
622622
"description": "Enable health check endpoint globally",
623623
"default": true,
624624
"additionalProperties": false
@@ -1391,7 +1391,15 @@
13911391
"type": "string"
13921392
}
13931393
},
1394-
"required": ["singular"]
1394+
"required": [ "singular" ]
1395+
}
1396+
]
1397+
},
1398+
"boolean-or-string": {
1399+
"oneOf":[
1400+
{
1401+
"type": [ "boolean", "string" ],
1402+
"pattern": "^(?:true|false|1|0|@env\\('.*'\\)|@akv\\('.*'\\))$"
13951403
}
13961404
]
13971405
},

src/Azure.DataApiBuilder.Mcp/BuiltInTools/CreateRecordTool.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ public async Task<CallToolResult> ExecuteAsync(
8383
return McpResponseBuilder.BuildErrorResult(toolName, "InvalidArguments", parseError, logger);
8484
}
8585

86+
// Check entity-level DML tool configuration
87+
if (runtimeConfig.Entities?.TryGetValue(entityName, out Entity? entity) == true &&
88+
entity.Mcp?.DmlToolEnabled == false)
89+
{
90+
return McpErrorHelpers.ToolDisabled(toolName, logger, $"DML tools are disabled for entity '{entityName}'.");
91+
}
92+
8693
if (!McpMetadataHelper.TryResolveMetadata(
8794
entityName,
8895
runtimeConfig,

src/Azure.DataApiBuilder.Mcp/BuiltInTools/DeleteRecordTool.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ public async Task<CallToolResult> ExecuteAsync(
101101
return McpResponseBuilder.BuildErrorResult(toolName, "InvalidArguments", parseError, logger);
102102
}
103103

104+
// Check entity-level DML tool configuration
105+
if (config.Entities?.TryGetValue(entityName, out Entity? entity) == true &&
106+
entity.Mcp?.DmlToolEnabled == false)
107+
{
108+
return McpErrorHelpers.ToolDisabled(toolName, logger, $"DML tools are disabled for entity '{entityName}'.");
109+
}
110+
104111
// 4) Resolve metadata for entity existence
105112
if (!McpMetadataHelper.TryResolveMetadata(
106113
entityName,

src/Azure.DataApiBuilder.Mcp/BuiltInTools/ExecuteEntityTool.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,18 @@ public async Task<CallToolResult> ExecuteAsync(
107107
return McpResponseBuilder.BuildErrorResult(toolName, "InvalidArguments", "Entity is required", logger);
108108
}
109109

110+
// Check entity-level DML tool configuration early (before metadata resolution)
111+
if (config.Entities?.TryGetValue(entity, out Entity? entityForCheck) == true &&
112+
entityForCheck.Mcp?.DmlToolEnabled == false)
113+
{
114+
return McpErrorHelpers.ToolDisabled(toolName, logger, $"DML tools are disabled for entity '{entity}'.");
115+
}
116+
110117
IMetadataProviderFactory metadataProviderFactory = serviceProvider.GetRequiredService<IMetadataProviderFactory>();
111118
IQueryEngineFactory queryEngineFactory = serviceProvider.GetRequiredService<IQueryEngineFactory>();
112119

113120
// 4) Validate entity exists and is a stored procedure
114-
if (!config.Entities.TryGetValue(entity, out Entity? entityConfig))
121+
if (config.Entities is null || !config.Entities.TryGetValue(entity, out Entity? entityConfig))
115122
{
116123
return McpResponseBuilder.BuildErrorResult(toolName, "EntityNotFound", $"Entity '{entity}' not found in configuration.", logger);
117124
}

src/Azure.DataApiBuilder.Mcp/BuiltInTools/ReadRecordsTool.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ public async Task<CallToolResult> ExecuteAsync(
114114
return McpResponseBuilder.BuildErrorResult(toolName, "InvalidArguments", parseError, logger);
115115
}
116116

117+
// Check entity-level DML tool configuration
118+
if (runtimeConfig.Entities?.TryGetValue(entityName, out Entity? entity) == true &&
119+
entity.Mcp?.DmlToolEnabled == false)
120+
{
121+
return McpErrorHelpers.ToolDisabled(toolName, logger, $"DML tools are disabled for entity '{entityName}'.");
122+
}
123+
117124
if (root.TryGetProperty("select", out JsonElement selectElement))
118125
{
119126
select = selectElement.GetString();

src/Azure.DataApiBuilder.Mcp/BuiltInTools/UpdateRecordTool.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ public async Task<CallToolResult> ExecuteAsync(
115115
return McpResponseBuilder.BuildErrorResult(toolName, "InvalidArguments", parseError, logger);
116116
}
117117

118+
// Check entity-level DML tool configuration
119+
if (config.Entities?.TryGetValue(entityName, out Entity? entity) == true &&
120+
entity.Mcp?.DmlToolEnabled == false)
121+
{
122+
return McpErrorHelpers.ToolDisabled(toolName, logger, $"DML tools are disabled for entity '{entityName}'.");
123+
}
124+
118125
IMetadataProviderFactory metadataProviderFactory = serviceProvider.GetRequiredService<IMetadataProviderFactory>();
119126
IMutationEngineFactory mutationEngineFactory = serviceProvider.GetRequiredService<IMutationEngineFactory>();
120127

src/Azure.DataApiBuilder.Mcp/Core/DynamicCustomTool.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ public async Task<CallToolResult> ExecuteAsync(
124124
return McpResponseBuilder.BuildErrorResult(toolName, "InvalidEntity", $"Entity {_entityName} is not a stored procedure.", logger);
125125
}
126126

127+
// Check if custom tool is still enabled for this entity
128+
if (entityConfig.Mcp?.CustomToolEnabled != true)
129+
{
130+
return McpErrorHelpers.ToolDisabled(toolName, logger, $"Custom tool is disabled for entity '{_entityName}'.");
131+
}
132+
127133
// 4) Resolve metadata
128134
if (!McpMetadataHelper.TryResolveMetadata(
129135
_entityName,

0 commit comments

Comments
 (0)