Skip to content
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

OpenAPI: Fix generation of correct fields #21942

Merged
merged 2 commits into from
Jul 25, 2023

Conversation

maxb
Copy link
Contributor

@maxb maxb commented Jul 19, 2023

Currently, the OpenAPI generator logic is wrong about how it maps from
Vault framework fields to OpenAPI. This manifests most obviously with
endpoints making use of framework.OptionalParamRegex or similar
regex-level optional path parameters, and results in various incorrect
fields showing up in the generated request structures.

The fix is a bit complicated, but in essence is just rewriting the
OpenAPI logic to properly parallel the real request processing logic.

With these changes:

  • A path parameter in an optional part of the regex, no longer gets
    erroneously treated as a body parameter when creating OpenAPI
    endpoints that do not include the optional parameter.

  • A field marked as Query: true no longer gets incorrectly skipped
    when creating OpenAPI POST operations.

maxb added 2 commits July 19, 2023 15:19
Currently, the OpenAPI generator logic is wrong about how it maps from
Vault framework fields to OpenAPI. This manifests most obviously with
endpoints making use of `framework.OptionalParamRegex` or similar
regex-level optional path parameters, and results in various incorrect
fields showing up in the generated request structures.

The fix is a bit complicated, but in essence is just rewriting the
OpenAPI logic to properly parallel the real request processing logic.

With these changes:

* A path parameter in an optional part of the regex, no longer gets
  erroneously treated as a body parameter when creating OpenAPI
  endpoints that do not include the optional parameter.

* A field marked as `Query: true` no longer gets incorrectly skipped
  when creating OpenAPI `POST` operations.
@maxb
Copy link
Contributor Author

maxb commented Jul 19, 2023

Please consult hashicorp/vault-client-go#199 for a more direct view of the results of this change, and why we need it.

@averche averche self-requested a review July 19, 2023 14:53
@averche averche self-assigned this Jul 19, 2023

if field == nil {
continue
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewer: I have deliberately deleted this nil check. I am confident it is redundant. There is no reason for this map to contain nil values, and furthermore, the other field maps are not similarly checked. If I was wrong about that, the tests, which do test generation of the full OpenAPI document, would have revealed the issue.

@@ -268,34 +268,22 @@ func documentPath(p *Path, backend *Backend, requestResponsePrefix string, doc *

// Process path and header parameters, which are common to all operations.
// Body fields will be added to individual operations.
pathFields, bodyFields := splitFields(p.Fields, path)
pathFields, queryFields, bodyFields := splitFields(p.Fields, path, captures)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewer: expandPattern will now collect extra information about capture groups found in the regex, which gets relayed to splitFields. splitFields will now make use of that, and cleanly separate path parameters from Query: true parameters, which were previously being combined into pathFields.

location = "query"
required = false
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewer: This loop is now handling path parameters exclusively.

// prepare the request body definition
case logical.CreateOperation:
fallthrough
case logical.UpdateOperation:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewer: Swapping if for switch because I need to increase the number of cases, and can make good use of fallthrough.

if field.Required {
s.Required = append(s.Required, name)
}
addFieldToOASSchema(s, name, field)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewer: the removed code here is being factored out to addFieldToOASSchema so that I can call it twice, once ranging over bodyFields, once ranging over queryFields.

Deprecated: field.Deprecated,
}
op.Parameters = append(op.Parameters, p)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewer: This added code is an approximate copy of the version handling path parameters at the top of this function.

func expandPattern(pattern string) ([]string, error) {
// and changing named parameters into their {openapi} equivalents. It also returns the names of all capturing groups
// observed in the pattern.
func expandPattern(pattern string) (paths []string, captures map[string]struct{}, err error) {
// Happily, the Go regexp library exposes its underlying "parse to AST" functionality, so we can rely on that to do
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewer: The next several chunks of changes, throughout expandPattern, collectPathsFromRegexpAST and collectPathsFromRegexpASTInternal are all just about recording the name of every observed regex capture group, and returning that data through several layers of function.

allFields map[string]*FieldSchema,
openAPIPathPattern string,
captures map[string]struct{},
) (pathFields, queryFields, bodyFields map[string]*FieldSchema) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewer: And here, right at the end of the diff, is the actual point of this PR - to correctly categorize all defined fields, in the same way the actual request processing will.

@averche averche added this to the 1.15 milestone Jul 25, 2023
@averche
Copy link
Contributor

averche commented Jul 25, 2023

Please consult hashicorp/vault-client-go#199 for a more direct view of the results of this change, and why we need it.

This was super helpful to understand the changes, thanks!

Copy link
Contributor

@averche averche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot again for this work, @maxb! I had previously noticed the potential issues with /v1/sys/tools/random/{source}/{urlbytes} and similar requests but was not brave enough to attempt a fix. This is awesome! 🎉

@averche averche merged commit 8e4409d into hashicorp:main Jul 25, 2023
@maxb maxb deleted the fix-openapi-fields branch July 25, 2023 03:40
maxb added a commit to maxb/vault that referenced this pull request Jul 25, 2023
In my recent hashicorp#21942, I overlooked the need to sort another part of the
OpenAPI document to ensure stable output.

I've also removed `strings.ToLower()` from the code I copied from, as
this code is sorting Vault API parameter names, which are all lowercase
anyway!
averche pushed a commit that referenced this pull request Jul 25, 2023
In my recent #21942, I overlooked the need to sort another part of the
OpenAPI document to ensure stable output.

I've also removed `strings.ToLower()` from the code I copied from, as
this code is sorting Vault API parameter names, which are all lowercase
anyway!
averche pushed a commit to hashicorp/vault-client-go that referenced this pull request Jul 25, 2023
This includes the results of:
* hashicorp/vault#21949
* hashicorp/vault#21942
* hashicorp/vault#22027

The general direction of change is pretty positive, but in reviewing the
generated diffs, I have spotted an issue: we are now generating correct
parameters for query parameters... but those query parameters are being
handled as individual function parameters... which are generated in the
order listed in the OpenAPI spec... which is dependent on random Go hash
iteration ordering. Ugh.

Oh well, let's catch up with latest developments in the Vault repo for
now, and I'll go put in a new PR to at least sort the parameters
alphabetically.

Longer term, this is probably going to push us in the direction of
excluding the GET version of APIs with equivalent GET and POST versions
from the generated libraries.
averche pushed a commit to hashicorp/vault-client-dotnet that referenced this pull request Jul 25, 2023
…150)

* Sync OpenAPI; Multiple parameter correctness changes

This includes the results of:
* hashicorp/vault#21949
* hashicorp/vault#21942
* hashicorp/vault#22027

The general direction of change is pretty positive, but in reviewing the
generated diffs, I have spotted an issue: we are now generating correct
parameters for query parameters... but those query parameters are being
handled as individual function parameters... which are generated in the
order listed in the OpenAPI spec... which is dependent on random Go hash
iteration ordering. Ugh.

Oh well, let's catch up with latest developments in the Vault repo for
now, and I'll go put in a new PR to at least sort the parameters
alphabetically.

Longer term, this is probably going to push us in the direction of
excluding the GET version of APIs with equivalent GET and POST versions
from the generated libraries.

* Update the templates with a missing using directive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants