Skip to content

Commit 4c5038f

Browse files
authored
[MCP] Added parameter description. (#2904)
## Why make this change? - Expands stored procedure parameter definitions in the configuration file to support richer metadata, using parameter's attributes like, `name`, `description`, `required`, and `default` values. - Enables MCP and other tooling to leverage parameter metadata for improved documentation, validation, and user experience. - Supports backward compatibility by allowing both the legacy dictionary format and the new array-of-objects format for parameters. ## What is this change? - Changes the `parameters` property in entity config from a dictionary mapping parameter names to values, to an array of parameter objects with explicit fields (`name`, `required`, `default`, `description`). - Updates the JSON schema to support the new structure and maintain support for the old syntax. - Refactors the internal model to handle both formats and expose richer metadata. - Enhances CLI commands (`dab add` and `dab update`) to accept and manage parameter metadata fields: `name`, `required`, `default`, and `description`. - Adds support for the expanded parameter object in CLI validation, including deprecation notes for the legacy format. - Includes parameter descriptions in DAB's OpenAPI and GraphQL schema outputs for improved API documentation. - This change also introduces smarter handling of optional parameters for stored procedures. If a parameter is not required and neither a user value nor a config default is provided, DAB will skip sending that parameter, allowing the database’s internal default to apply. This avoids duplication and drift between config and database defaults. The old behavior is fully supported for backward compatibility. ## How was this tested? - [x] Manual Testing: - Used CLI commands to add, validate and update parameters with metadata. - Verified config parsing, CLI commands, and schema generation for both legacy and expanded parameter formats. - Confirmed correct behavior in OpenAPI and GraphQL outputs. ## Sample Request(s) **Before (legacy dictionary format):** ```json { "entities": { "User": { "source": { "parameters": { "param1": "default-value" } } } } } ``` **After (expanded array format):** ```json { "entities": { "User": { "source": { "parameters": [ { "name": "Id", "required": true, "default": "default-value", "description": "The unique identifier for the user." } ] } } } } ``` **Command Line Examples:** **Before (legacy dictionary format):** ``` dotnet C:\DAB\data-api-builder\src\out\cli\net8.0\Microsoft.DataApiBuilder.dll add MyProc --source "dbo.MyProc" --source.type stored-procedure --source.params "PageSize:50,SortOrder:ASC" --permissions "anonymous:execute" "authenticated:execute" --config C:\DAB\data-api-builder\src\Service\dab-config.json ``` ``` "MyProc": { "source": { "object": "dbo.MyProc", "type": "stored-procedure", "parameters": [ { "name": "PageSize", "required": false, "default": "50" }, { "name": "SortOrder", "required": false, "default": "ASC" } ] }, "graphql": { "enabled": true, "operation": "mutation", "type": { "singular": "MyProc", "plural": "MyProcs" } }, "rest": { "enabled": true, "methods": [ "post" ] }, "permissions": [ { "role": "anonymous", "actions": [ { "action": "execute" } ] } ] } ``` **After (expanded array format):** Add: ``` dotnet C:\DAB\data-api-builder\src\out\cli\net8.0\Microsoft.DataApiBuilder.dll add entity GetTodosByOwnerAndStatus --source "dbo.GetTodosByOwnerAndStatus" --source.type stored-procedure --parameters.name "OwnerId,Completed" --parameters.description "Owner ID,Completed status" --parameters.required "true,false" --parameters.default "public,0" --permissions "anonymous:execute" --config C:\DAB\data-api-builder\src\Service\dab-config.json ``` Config created: ``` "entity": { "source": { "object": "dbo.GetTodosByOwnerAndStatus", "type": "stored-procedure", "parameters": [ { "name": "OwnerId", "description": "Owner ID", "required": true, "default": "public" }, { "name": "Completed", "description": "Completed status", "required": false, "default": "0" } ] }, "graphql": { "enabled": true, "operation": "mutation", "type": { "singular": "entity", "plural": "entities" } }, "rest": { "enabled": true, "methods": [ "post" ] }, "permissions": [ { "role": "anonymous", "actions": [ { "action": "execute" } ] } ] } ``` Validate: ``` dotnet C:\DAB\data-api-builder\src\out\cli\net8.0\Microsoft.DataApiBuilder.dll validate -c "C:\DAB\data-api-builder\src\Service\dab-config.json" ``` Validate when both formats are given: ``` PS C:\DAB\data-api-builder\src\Service> dotnet C:\DAB\data-api-builder\src\out\cli\net8.0\Microsoft.DataApiBuilder.dll add MyProc --source "dbo.MyProc" --source.type stored-procedure --source.params "PageSize:50,SortOrder:ASC" --permissions "anonymous:execute" "authenticated:execute" --config C:\DAB\data-api-builder\src\Service\dab-config.json --parameters.name "OwnerId,Completed" --parameters.description "Owner ID,Completed status" --parameters.required "true,false" --parameters.default "public,0" Information: Microsoft.DataApiBuilder 1.7.0 Information: User provided config file: C:\DAB\data-api-builder\src\Service\dab-config.json Loading config file from C:\DAB\data-api-builder\src\Service\dab-config.json. Error: Cannot use both --source.params and --parameters.name/description/required/default together. Please use only one format. Error: Unable to create the source object. Error: Failed to add a new entity. Error: Could not add entity: MyProc with source: dbo.MyProc and permissions: anonymous:execute. ``` Update: ``` dotnet C:\DAB\data-api-builder\src\out\cli\net8.0\Microsoft.DataApiBuilder.dll update entity GetTodosByOwnerAndStatus --parameters.name "OwnerId,Completed" --parameters.description "Owner ID,Completed status" --parameters.required "true,false" --parameters.default "private,protected" --config "C:\DAB\data-api-builder\src\Service\dab-config.json" ``` Config updated: ``` "entity": { "source": { "object": "dbo.GetTodosByOwnerAndStatus", "type": "stored-procedure", "parameters": [ { "name": "OwnerId", "description": "Owner ID", "required": true, "default": "private" }, { "name": "Completed", "description": "Completed status", "required": false, "default": "protected" } ] }, "graphql": { "enabled": true, "operation": "mutation", "type": { "singular": "entity", "plural": "entities" } }, "rest": { "enabled": true, "methods": [ "post" ] }, "permissions": [ { "role": "anonymous", "actions": [ { "action": "execute" } ] } ] } ``` ----------------------------------------------- Create a stored procedure: ``` CREATE PROCEDURE dbo.InsertTodo @id UNIQUEIDENTIFIER, @title NVARCHAR(1000), @completed BIT = 0, @owner_id VARCHAR(128), @position INT = NULL AS BEGIN INSERT INTO dbo.todos (id, title, completed, owner_id, position) VALUES (@id, @title, @completed, @owner_id, @position); SELECT id, title, completed, owner_id, position FROM dbo.todos WHERE id = @id; END GO ``` 1. Without providing default parameter values ``` mutation { executeInsertTodo( id: "00000000-0000-0000-0000-000000000013", title: "Test DAB GraphQL", owner_id: "Anusha Kolan" # completed and position are optional ) { id title completed owner_id position } } ``` Uses DB default values ``` { "data": { "executeInsertTodo": [ { "id": "00000000-0000-0000-0000-000000000013", "title": "Test DAB GraphQL", "completed": false, "owner_id": "Anusha Kolan", "position": null } ] } } ``` 2. With providing default parameter values ``` mutation { executeInsertTodo( id: "00000000-0000-0000-0000-000000000014", title: "Test with completed", owner_id: "Anusha Kolan", completed: true, position: 5 ) { id title completed owner_id position } } ``` Uses the default values provided ``` { "data": { "executeInsertTodo": [ { "id": "00000000-0000-0000-0000-000000000014", "title": "Test with completed", "completed": true, "owner_id": "Anusha Kolan", "position": 5 } ] } } ``` ----------------------------------------------------------------------------------- Config: ``` "InsertTodo": { "source": { "object": "dbo.InsertTodo", "type": "stored-procedure", "parameters": [ { "name": "id", "description": "The id for the Todo.", "required": true }, { "name": "title", "description": "The title for the Todo.", "required": true }, { "name": "completed", "description": "The state of the Todo.", "required": false }, { "name": "owner_id", "required": true }, { "name": "position", "required": false } ] }, "graphql": { "enabled": true, "operation": "mutation", "type": { "singular": "InsertTodo", "plural": "InsertTodos" } }, "rest": { "enabled": true, "methods": [ "post" ] }, "permissions": [ { "role": "anonymous", "actions": [ { "action": "execute" } ] } ] } ``` GraphQL Schema Documentation ``` "Execute Stored-Procedure InsertTodo and get results from the database" executeInsertTodo( "The state of the Todo." completed: Boolean "The id for the Todo." id: UUID "parameters for InsertTodo stored-procedure" owner_id: String "parameters for InsertTodo stored-procedure" position: Int "The title for the Todo." title: String ): [InsertTodo!]! @cost(weight: "10") ``` OpenAPI Documentation ``` "InsertTodo_sp_request": { "required": [ "id", "owner_id", "title" ], "type": "object", "properties": { "completed": { "type": "boolean", "description": "The state of the Todo." }, "id": { "type": "string", "description": "The id for the Todo." }, "owner_id": { "type": "string" }, "position": { "type": "number" }, "title": { "type": "string", "description": "The title for the Todo." } } }, ```
1 parent 505e0c2 commit 4c5038f

34 files changed

Lines changed: 753 additions & 224 deletions

File tree

schemas/dab.draft.schema.json

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -755,23 +755,35 @@
755755
"description": "Database object name"
756756
},
757757
"parameters": {
758-
"type": "object",
759-
"description": "Dictionary of parameters and their values",
760-
"patternProperties": {
761-
"^.*$": {
762-
"oneOf": [
763-
{
764-
"type": "boolean"
765-
},
766-
{
767-
"type": "string"
768-
},
769-
{
770-
"type": "number"
758+
"oneOf": [
759+
{
760+
"type": "object",
761+
"description": "Dictionary of parameters and their values (deprecated)",
762+
"patternProperties": {
763+
"^.*$": {
764+
"oneOf": [
765+
{ "type": "boolean" },
766+
{ "type": "string" },
767+
{ "type": "number" }
768+
]
771769
}
772-
]
770+
}
771+
},
772+
{
773+
"type": "array",
774+
"description": "Array of parameter objects with metadata",
775+
"items": {
776+
"type": "object",
777+
"required": ["name"],
778+
"properties": {
779+
"name": { "type": "string", "description": "Parameter name" },
780+
"required": { "type": "boolean", "description": "Is parameter required" },
781+
"default": { "type": ["string", "number", "boolean", "null"], "description": "Default value" },
782+
"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 (',')." }
783+
}
784+
}
773785
}
774-
}
786+
]
775787
},
776788
"key-fields": {
777789
"type": "array",

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ public async Task<CallToolResult> ExecuteAsync(
169169
// Validate all provided parameters exist in metadata
170170
foreach (KeyValuePair<string, object?> param in parameters)
171171
{
172-
if (!entityConfig.Source.Parameters.ContainsKey(param.Key))
172+
if (!entityConfig.Source.Parameters.Any(p => p.Name == param.Key))
173173
{
174174
return McpResponseBuilder.BuildErrorResult("InvalidArguments", $"Invalid parameter: {param.Key}", logger);
175175
}
@@ -205,11 +205,11 @@ public async Task<CallToolResult> ExecuteAsync(
205205
// Then, add default parameters from configuration (only if not already provided by user)
206206
if ((parameters == null || parameters.Count == 0) && entityConfig.Source.Parameters != null)
207207
{
208-
foreach (KeyValuePair<string, object> param in entityConfig.Source.Parameters)
208+
foreach (ParameterMetadata param in entityConfig.Source.Parameters)
209209
{
210-
if (!context.FieldValuePairsInBody.ContainsKey(param.Key))
210+
if (!context.FieldValuePairsInBody.ContainsKey(param.Name))
211211
{
212-
context.FieldValuePairsInBody[param.Key] = param.Value;
212+
context.FieldValuePairsInBody[param.Name] = param.Default;
213213
}
214214
}
215215
}

src/Cli.Tests/AddEntityTests.cs

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,12 @@ public Task AddNewEntityWhenEntitiesEmpty()
4545
cacheTtl: null,
4646
config: TEST_RUNTIME_CONFIG_FILE,
4747
restMethodsForStoredProcedure: null,
48-
graphQLOperationForStoredProcedure: null
48+
graphQLOperationForStoredProcedure: null,
49+
parametersNameCollection: null,
50+
parametersDescriptionCollection: null,
51+
parametersRequiredCollection: null,
52+
parametersDefaultCollection: null
4953
);
50-
5154
return ExecuteVerifyTest(options);
5255
}
5356

@@ -75,7 +78,11 @@ public Task AddNewEntityWhenEntitiesNotEmpty()
7578
cacheTtl: null,
7679
config: TEST_RUNTIME_CONFIG_FILE,
7780
restMethodsForStoredProcedure: null,
78-
graphQLOperationForStoredProcedure: null
81+
graphQLOperationForStoredProcedure: null,
82+
parametersNameCollection: null,
83+
parametersDescriptionCollection: null,
84+
parametersRequiredCollection: null,
85+
parametersDefaultCollection: null
7986
);
8087

8188
string initialConfiguration = AddPropertiesToJson(INITIAL_CONFIG, GetFirstEntityConfiguration());
@@ -107,7 +114,11 @@ public void AddDuplicateEntity()
107114
cacheTtl: null,
108115
config: TEST_RUNTIME_CONFIG_FILE,
109116
restMethodsForStoredProcedure: null,
110-
graphQLOperationForStoredProcedure: null
117+
graphQLOperationForStoredProcedure: null,
118+
parametersNameCollection: null,
119+
parametersDescriptionCollection: null,
120+
parametersRequiredCollection: null,
121+
parametersDefaultCollection: null
111122
);
112123

113124
string initialConfiguration = AddPropertiesToJson(INITIAL_CONFIG, GetFirstEntityConfiguration());
@@ -143,7 +154,11 @@ public Task AddEntityWithAnExistingNameButWithDifferentCase()
143154
cacheTtl: null,
144155
config: TEST_RUNTIME_CONFIG_FILE,
145156
restMethodsForStoredProcedure: null,
146-
graphQLOperationForStoredProcedure: null
157+
graphQLOperationForStoredProcedure: null,
158+
parametersNameCollection: null,
159+
parametersDescriptionCollection: null,
160+
parametersRequiredCollection: null,
161+
parametersDefaultCollection: null
147162
);
148163

149164
string initialConfiguration = AddPropertiesToJson(INITIAL_CONFIG, GetFirstEntityConfiguration());
@@ -174,7 +189,11 @@ public Task AddEntityWithCachingEnabled()
174189
cacheTtl: "1",
175190
config: TEST_RUNTIME_CONFIG_FILE,
176191
restMethodsForStoredProcedure: null,
177-
graphQLOperationForStoredProcedure: null
192+
graphQLOperationForStoredProcedure: null,
193+
parametersNameCollection: null,
194+
parametersDescriptionCollection: null,
195+
parametersRequiredCollection: null,
196+
parametersDefaultCollection: null
178197
);
179198

180199
return ExecuteVerifyTest(options);
@@ -211,7 +230,11 @@ public Task AddEntityWithPolicyAndFieldProperties(
211230
cacheEnabled: null,
212231
cacheTtl: null,
213232
restMethodsForStoredProcedure: null,
214-
graphQLOperationForStoredProcedure: null
233+
graphQLOperationForStoredProcedure: null,
234+
parametersNameCollection: null,
235+
parametersDescriptionCollection: null,
236+
parametersRequiredCollection: null,
237+
parametersDefaultCollection: null
215238
);
216239

217240
// Create VerifySettings and add all arguments to the method as parameters
@@ -244,7 +267,11 @@ public Task AddNewEntityWhenEntitiesWithSourceAsStoredProcedure()
244267
cacheEnabled: null,
245268
cacheTtl: null,
246269
restMethodsForStoredProcedure: null,
247-
graphQLOperationForStoredProcedure: null
270+
graphQLOperationForStoredProcedure: null,
271+
parametersNameCollection: null,
272+
parametersDescriptionCollection: ["This is a test parameter description."],
273+
parametersRequiredCollection: null,
274+
parametersDefaultCollection: null
248275
);
249276

250277
return ExecuteVerifyTest(options);
@@ -276,7 +303,11 @@ public Task TestAddStoredProcedureWithRestMethodsAndGraphQLOperations()
276303
cacheTtl: null,
277304
config: TEST_RUNTIME_CONFIG_FILE,
278305
restMethodsForStoredProcedure: new string[] { "Post", "Put", "Patch" },
279-
graphQLOperationForStoredProcedure: "Query"
306+
graphQLOperationForStoredProcedure: "Query",
307+
parametersNameCollection: null,
308+
parametersDescriptionCollection: null,
309+
parametersRequiredCollection: null,
310+
parametersDefaultCollection: null
280311
);
281312

282313
return ExecuteVerifyTest(options);
@@ -304,7 +335,11 @@ public void AddEntityWithDescriptionAndVerifyInConfig()
304335
cacheTtl: null,
305336
config: TEST_RUNTIME_CONFIG_FILE,
306337
restMethodsForStoredProcedure: null,
307-
graphQLOperationForStoredProcedure: null
338+
graphQLOperationForStoredProcedure: null,
339+
parametersNameCollection: null,
340+
parametersDescriptionCollection: null,
341+
parametersRequiredCollection: null,
342+
parametersDefaultCollection: null
308343
);
309344

310345
string config = INITIAL_CONFIG;
@@ -359,7 +394,11 @@ public void TestAddNewEntityWithSourceObjectHavingValidFields(
359394
cacheTtl: null,
360395
config: TEST_RUNTIME_CONFIG_FILE,
361396
restMethodsForStoredProcedure: null,
362-
graphQLOperationForStoredProcedure: null
397+
graphQLOperationForStoredProcedure: null,
398+
parametersNameCollection: null,
399+
parametersDescriptionCollection: null,
400+
parametersRequiredCollection: null,
401+
parametersDefaultCollection: null
363402
);
364403

365404
RuntimeConfigLoader.TryParseConfig(INITIAL_CONFIG, out RuntimeConfig? runtimeConfig);
@@ -419,7 +458,11 @@ public Task TestAddNewSpWithDifferentRestAndGraphQLOptions(
419458
cacheTtl: null,
420459
config: TEST_RUNTIME_CONFIG_FILE,
421460
restMethodsForStoredProcedure: restMethods,
422-
graphQLOperationForStoredProcedure: graphQLOperation
461+
graphQLOperationForStoredProcedure: graphQLOperation,
462+
parametersNameCollection: null,
463+
parametersDescriptionCollection: null,
464+
parametersRequiredCollection: null,
465+
parametersDefaultCollection: null
423466
);
424467

425468
VerifySettings settings = new();
@@ -455,7 +498,11 @@ public void TestAddStoredProcedureWithConflictingRestGraphQLOptions(
455498
cacheTtl: null,
456499
config: TEST_RUNTIME_CONFIG_FILE,
457500
restMethodsForStoredProcedure: restMethods,
458-
graphQLOperationForStoredProcedure: graphQLOperation
501+
graphQLOperationForStoredProcedure: graphQLOperation,
502+
parametersNameCollection: null,
503+
parametersDescriptionCollection: null,
504+
parametersRequiredCollection: null,
505+
parametersDefaultCollection: null
459506
);
460507

461508
RuntimeConfigLoader.TryParseConfig(INITIAL_CONFIG, out RuntimeConfig? runtimeConfig);
@@ -494,7 +541,11 @@ public void TestAddEntityPermissionWithInvalidOperation(IEnumerable<string> perm
494541
cacheTtl: null,
495542
config: TEST_RUNTIME_CONFIG_FILE,
496543
restMethodsForStoredProcedure: null,
497-
graphQLOperationForStoredProcedure: null
544+
graphQLOperationForStoredProcedure: null,
545+
parametersNameCollection: null,
546+
parametersDescriptionCollection: null,
547+
parametersRequiredCollection: null,
548+
parametersDefaultCollection: null
498549
);
499550

500551
RuntimeConfigLoader.TryParseConfig(INITIAL_CONFIG, out RuntimeConfig? runtimeConfig);

src/Cli.Tests/Snapshots/AddEntityTests.AddNewEntityWhenEntitiesWithSourceAsStoredProcedure.verified.txt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,23 @@
2828
Source: {
2929
Object: s001.book,
3030
Type: stored-procedure,
31-
Parameters: {
32-
param1: 123,
33-
param2: hello,
34-
param3: true
35-
}
31+
Parameters: [
32+
{
33+
Name: param1,
34+
Required: false,
35+
Default: 123
36+
},
37+
{
38+
Name: param2,
39+
Required: false,
40+
Default: hello
41+
},
42+
{
43+
Name: param3,
44+
Required: false,
45+
Default: True
46+
}
47+
]
3648
},
3749
GraphQL: {
3850
Singular: MyEntity,

src/Cli.Tests/Snapshots/AddEntityTests.TestAddStoredProcedureWithRestMethodsAndGraphQLOperations.verified.txt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,23 @@
2828
Source: {
2929
Object: s001.book,
3030
Type: stored-procedure,
31-
Parameters: {
32-
param1: 123,
33-
param2: hello,
34-
param3: true
35-
}
31+
Parameters: [
32+
{
33+
Name: param1,
34+
Required: false,
35+
Default: 123
36+
},
37+
{
38+
Name: param2,
39+
Required: false,
40+
Default: hello
41+
},
42+
{
43+
Name: param3,
44+
Required: false,
45+
Default: True
46+
}
47+
]
3648
},
3749
GraphQL: {
3850
Singular: MyEntity,

src/Cli.Tests/Snapshots/EndToEndTests.TestAddingStoredProcedureWithRestMethodsAndGraphQLOperations.verified.txt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,23 @@
3535
Source: {
3636
Object: s001.book,
3737
Type: stored-procedure,
38-
Parameters: {
39-
param1: 123,
40-
param2: hello,
41-
param3: true
42-
}
38+
Parameters: [
39+
{
40+
Name: param1,
41+
Required: false,
42+
Default: 123
43+
},
44+
{
45+
Name: param2,
46+
Required: false,
47+
Default: hello
48+
},
49+
{
50+
Name: param3,
51+
Required: false,
52+
Default: True
53+
}
54+
]
4355
},
4456
GraphQL: {
4557
Singular: MyEntity,

src/Cli.Tests/Snapshots/EndToEndTests.TestConfigGeneratedAfterAddingEntityWithSourceAsStoredProcedure.verified.txt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,23 @@
3535
Source: {
3636
Object: s001.book,
3737
Type: stored-procedure,
38-
Parameters: {
39-
param1: 123,
40-
param2: hello,
41-
param3: true
42-
}
38+
Parameters: [
39+
{
40+
Name: param1,
41+
Required: false,
42+
Default: 123
43+
},
44+
{
45+
Name: param2,
46+
Required: false,
47+
Default: hello
48+
},
49+
{
50+
Name: param3,
51+
Required: false,
52+
Default: True
53+
}
54+
]
4355
},
4456
GraphQL: {
4557
Singular: MyEntity,

src/Cli.Tests/Snapshots/EndToEndTests.TestConfigGeneratedAfterUpdatingEntityWithSourceAsStoredProcedure.verified.txt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,23 @@
22
Source: {
33
Object: dbo.books,
44
Type: stored-procedure,
5-
Parameters: {
6-
param1: 123,
7-
param2: hello,
8-
param3: true
9-
}
5+
Parameters: [
6+
{
7+
Name: param1,
8+
Required: false,
9+
Default: 123
10+
},
11+
{
12+
Name: param2,
13+
Required: false,
14+
Default: hello
15+
},
16+
{
17+
Name: param3,
18+
Required: false,
19+
Default: True
20+
}
21+
]
1022
},
1123
GraphQL: {
1224
Singular: MyEntity,

0 commit comments

Comments
 (0)