-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Conversation
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.
Please consult hashicorp/vault-client-go#199 for a more direct view of the results of this change, and why we need it. |
|
||
if field == nil { | ||
continue | ||
} |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 | ||
} | ||
|
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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) | ||
} |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
This was super helpful to understand the changes, thanks! |
There was a problem hiding this 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! 🎉
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!
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!
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.
…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
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 similarregex-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 skippedwhen creating OpenAPI
POST
operations.