-
Notifications
You must be signed in to change notification settings - Fork 356
Mark stored procedure parameters without defaults as required in OpenAPI #3687
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Copilot
wants to merge
16
commits into
main
Choose a base branch
from
copilot/fix-openapi-required-params
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+125
−8
Open
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
1a8edbe
Initial plan
Copilot 4467e79
Mark stored procedure params without defaults as required in OpenAPI
Copilot d6bef3b
Add tests for SP parameter required logic in OpenAPI schema
Copilot 3f4b30c
Merge branch 'main' into copilot/fix-openapi-required-params
souvikghosh04 9f797d5
Potential fix for pull request finding
souvikghosh04 8154a7a
Update src/Service.Tests/OpenApiDocumentor/StoredProcedureGeneration.cs
souvikghosh04 e68102a
Fix IsRequestBodyRequired stored-procedure branch to honor Required=f…
Copilot 6398a93
Merge branch 'main' into copilot/fix-openapi-required-params
souvikghosh04 ac1774b
Fix whitespace formatting in StoredProcedureGeneration.cs
Copilot d614c62
Merge branch 'main' into copilot/fix-openapi-required-params
souvikghosh04 3d38fb4
Merge branch 'main' into copilot/fix-openapi-required-params
souvikghosh04 ad554cd
Merge branch 'main' into copilot/fix-openapi-required-params
souvikghosh04 2d432dc
Fix GET SP query params required field; add requestBody.Required asse…
Copilot b8ea826
Merge branch 'main' into copilot/fix-openapi-required-params
souvikghosh04 bf073c4
Merge branch 'main' into copilot/fix-openapi-required-params
souvikghosh04 7c0b8a9
Merge branch 'main' into copilot/fix-openapi-required-params
souvikghosh04 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,9 +63,72 @@ public static void CreateEntities() | |
| Relationships: null, | ||
| Description: "Represents a stored procedure for books"); | ||
|
|
||
| // Entity whose "title" parameter has a config-provided default value. | ||
| // Because a default is available, "title" must not be flagged as required, while | ||
| // "publisher_id" (no default) must remain required. | ||
| Entity entity2 = new( | ||
| Source: new( | ||
| Object: "insert_book", | ||
| EntitySourceType.StoredProcedure, | ||
| Parameters: new List<ParameterMetadata> | ||
| { | ||
| new() { Name = "title", Default = "Sample Title" } | ||
| }, | ||
| KeyFields: null), | ||
| Fields: null, | ||
| GraphQL: new(Singular: null, Plural: null, Enabled: false), | ||
| Rest: new(Methods: EntityRestOptions.DEFAULT_SUPPORTED_VERBS), | ||
| Permissions: OpenApiTestBootstrap.CreateBasicPermissions(), | ||
| Mappings: null, | ||
| Relationships: null, | ||
| Description: "Stored procedure with a parameter default"); | ||
|
|
||
| // Entity whose "title" parameter is explicitly marked required: false in config. | ||
| // Even though it has no default value, the explicit override must be honored so that only | ||
| // "id" (no default, no override) remains required. | ||
| Entity entity3 = new( | ||
| Source: new( | ||
| Object: "update_book_title", | ||
| EntitySourceType.StoredProcedure, | ||
| Parameters: new List<ParameterMetadata> | ||
| { | ||
| new() { Name = "title", Required = false } | ||
| }, | ||
| KeyFields: null), | ||
| Fields: null, | ||
| GraphQL: new(Singular: null, Plural: null, Enabled: false), | ||
| Rest: new(Methods: EntityRestOptions.DEFAULT_SUPPORTED_VERBS), | ||
| Permissions: OpenApiTestBootstrap.CreateBasicPermissions(), | ||
| Mappings: null, | ||
| Relationships: null, | ||
| Description: "Stored procedure with an explicit required override"); | ||
|
|
||
| // Entity whose only parameter is explicitly marked required: false in config. | ||
| // Because no parameter is required, requestBody.Required and the GET query parameter's | ||
| // required flag must both be false, and the schema-level required set must be empty. | ||
| Entity entity4 = new( | ||
| Source: new( | ||
| Object: "get_publisher_by_id", | ||
| EntitySourceType.StoredProcedure, | ||
| Parameters: new List<ParameterMetadata> | ||
| { | ||
| new() { Name = "id", Required = false } | ||
| }, | ||
| KeyFields: null), | ||
| Fields: null, | ||
| GraphQL: new(Singular: null, Plural: null, Enabled: false), | ||
| Rest: new(Methods: EntityRestOptions.DEFAULT_SUPPORTED_VERBS), | ||
| Permissions: OpenApiTestBootstrap.CreateBasicPermissions(), | ||
| Mappings: null, | ||
| Relationships: null, | ||
| Description: "Stored procedure with all parameters marked required: false"); | ||
|
|
||
| Dictionary<string, Entity> entities = new() | ||
| { | ||
| { "sp1", entity1 } | ||
| { "sp1", entity1 }, | ||
| { "sp2", entity2 }, | ||
| { "sp3", entity3 }, | ||
| { "sp4", entity4 } | ||
| }; | ||
|
|
||
| _runtimeEntities = new(entities); | ||
|
|
@@ -74,13 +137,23 @@ public static void CreateEntities() | |
| /// <summary> | ||
| /// Validates that the generated request body references stored procedure parameters | ||
| /// and not result set columns. | ||
| /// Also validates that the request body schema component flags the expected parameters | ||
| /// as required: a parameter is required when it has no default value and is not explicitly | ||
| /// marked required: false in the runtime config. | ||
| /// Also validates the body-level required flag on the OpenApiRequestBody object, which | ||
| /// should be true when any parameter is required and false when all parameters are optional. | ||
| /// </summary> | ||
| /// <param name="entityName">Entity name</param> | ||
| /// <param name="expectedParameters">Expected parameters in request body</param> | ||
| /// <param name="expectedParametersJsonTypes">Expected parameter value types in request body.</param> | ||
| [DataRow("sp1", new string[] { "title", "publisher_name" }, new string[] { "string", "string" }, DisplayName = "Validate request body parameters and parameter Json data types.")] | ||
| /// <param name="expectedRequiredParameters">Expected parameters flagged as required in the schema component.</param> | ||
| /// <param name="expectedRequestBodyRequired">Expected value of the body-level required flag.</param> | ||
| [DataRow("sp1", new string[] { "title", "publisher_name" }, new string[] { "string", "string" }, new string[] { "title", "publisher_name" }, true, DisplayName = "Parameters without defaults are all required.")] | ||
| [DataRow("sp2", new string[] { "title", "publisher_id" }, new string[] { "string", "integer" }, new string[] { "publisher_id" }, true, DisplayName = "Parameter with a config default is not required.")] | ||
| [DataRow("sp3", new string[] { "id", "title" }, new string[] { "integer", "string" }, new string[] { "id" }, true, DisplayName = "Parameter explicitly marked required: false is not required.")] | ||
| [DataRow("sp4", new string[] { "id" }, new string[] { "integer" }, new string[] { }, false, DisplayName = "All parameters marked required: false produces empty required set and optional request body.")] | ||
| [DataTestMethod] | ||
| public void ValidateRequestBodyContents(string entityName, string[] expectedParameters, string[] expectedParametersJsonTypes) | ||
| public void ValidateRequestBodyContents(string entityName, string[] expectedParameters, string[] expectedParametersJsonTypes, string[] expectedRequiredParameters, bool expectedRequestBodyRequired) | ||
| { | ||
| Dictionary<OperationType, bool> configuredOperations = ResolveConfiguredOperations(_runtimeEntities[entityName]); | ||
| foreach (OperationType opType in configuredOperations.Keys) | ||
|
|
@@ -100,7 +173,17 @@ public void ValidateRequestBodyContents(string entityName, string[] expectedPara | |
| OpenApiReference schemaComponentReference = GetRequestBodyReference(requestBody); | ||
| string expectedSchemaReferenceId = $"{entityName}{OpenApiDocumentor.SP_REQUEST_SUFFIX}"; | ||
|
|
||
| // Validate the body-level required flag. | ||
| Assert.AreEqual(expectedRequestBodyRequired, requestBody.Required, message: "Unexpected request body required value."); | ||
|
|
||
| ValidateOpenApiReferenceContents(schemaComponentReference, expectedSchemaReferenceId, expectedParameters, expectedParametersJsonTypes); | ||
|
|
||
| // Validate that the expected parameters are marked as required in the schema component. | ||
| ISet<string> requiredParameters = _openApiDocument.Components.Schemas[expectedSchemaReferenceId].Required ?? new HashSet<string>(); | ||
| CollectionAssert.AreEquivalent( | ||
| expectedRequiredParameters, | ||
| requiredParameters.ToArray(), | ||
| message: "Unexpected required parameters in request body schema component."); | ||
| } | ||
|
souvikghosh04 marked this conversation as resolved.
|
||
| } | ||
|
|
||
|
|
@@ -149,6 +232,37 @@ public void OpenApiDocumentor_TagsIncludeEntityDescription() | |
| $"Expected tag for '{entityName}' with description '{expectedDescription}' not found."); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Validates that the generated GET operation query parameters for stored procedure entities | ||
| /// have the correct required flag: a parameter without a config default and not explicitly | ||
| /// marked required: false is required; a parameter explicitly marked required: false is not. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Change |
||
| /// </summary> | ||
| /// <param name="entityName">Entity name.</param> | ||
| /// <param name="expectedParameters">Expected query parameter names.</param> | ||
| /// <param name="expectedRequired">Whether each corresponding query parameter is expected to be required.</param> | ||
| [DataRow("sp1", new string[] { "title", "publisher_name" }, new bool[] { true, true }, DisplayName = "GET parameters without defaults are required.")] | ||
| [DataRow("sp4", new string[] { "id" }, new bool[] { false }, DisplayName = "GET parameter explicitly marked required: false is not required.")] | ||
| [DataTestMethod] | ||
| public void ValidateGetQueryParameters(string entityName, string[] expectedParameters, bool[] expectedRequired) | ||
| { | ||
| OpenApiOperation getOperation = _openApiDocument.Paths["/" + entityName].Operations[OperationType.Get]; | ||
| Assert.IsNotNull(getOperation, "GET operation not found."); | ||
|
|
||
| // Filter to query parameters only (excludes the Authorization and X-MS-API-ROLE header parameters). | ||
| List<OpenApiParameter> spParams = getOperation.Parameters | ||
| .Where(p => p.In == ParameterLocation.Query) | ||
| .ToList(); | ||
|
|
||
| Assert.AreEqual(expectedParameters.Length, spParams.Count, "Unexpected number of GET query parameters."); | ||
|
|
||
| for (int i = 0; i < expectedParameters.Length; i++) | ||
| { | ||
| OpenApiParameter param = spParams.FirstOrDefault(p => p.Name == expectedParameters[i]); | ||
| Assert.IsNotNull(param, $"Parameter '{expectedParameters[i]}' not found in GET query parameters."); | ||
| Assert.AreEqual(expectedRequired[i], param.Required, $"Unexpected required value for GET query parameter '{expectedParameters[i]}'."); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Validates that the provided OpenApiReference object has the expected schema reference id | ||
| /// and that that id is present in the list of component schema in the OpenApi document. | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.