Repository navigation
feat!: lower a response's links to the operations they lead to - #1375
Open
OmarAlJarrah wants to merge 3 commits into
Open
OmarAlJarrah wants to merge 3 commits into
OmarAlJarrah wants to merge 3 commits into
Conversation
An OpenAPI link says which operation a caller can call next and where each of its inputs comes from. The IR had no field for one, so the compiler kept a response's whole links map verbatim, and a consumer wanting a follow call had to parse OpenAPI's runtime expressions and resolve them against the types itself. Response and ErrorCase now carry Links. An OperationLink names the operation it leads to, the value it gives each of that operation's parameters and its body, and the server it is called on. A LinkValue is a constant, the URL, method or status of the exchange, or a value of the request answered or of the response, resolved to an InputPath or a PropPath, so members are named by ID rather than by wire name. The OpenAPI compiler resolves links once every operation has lowered and the type registry is whole, since a link may name an operation declared after it. A link it cannot resolve whole is kept verbatim under openapi:links/<name>, with an openapi/invalid-link warning when it names nothing the document declares or breaks the expression grammar, and an openapi/untyped-link info when it is sound but reads what no link value spells, such as a member through an array index. Neither fails a compile: nothing is lost. A $ref in a links map is now followed, so the components/links entry it names is no longer also kept on the document. An InputPath with neither a parameter nor a segment is now the request body whole, which a link can pass on; ir/input-path-empty is gone. pass.Validate holds a link's parameters to its target and its request values to the operation answered, and irverify holds a link value's field to its kind. BREAKING CHANGE: IRVersion moves to 0.35.0. A consumer pinned to 0.34.0 refuses a document with a link, and finds no openapi:links entry where it read one.
Two descriptions of a pagination input still said it was a parameter or a body member, where an InputPath may now name the request body whole.
…s' into feat/ir-response-links
17 tasks done
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
An OpenAPI link says which operation a caller can call next and where each of its inputs comes from, which docs need for "follow with
getOrder" and an SDK needs for a follow call. The IR had no field for one, so the compiler kept a response's wholelinksmap verbatim underopenapi:links, and a consumer had to parse OpenAPI's runtime expressions and resolve them against the types itself.ResponseandErrorCasegainLinks []OperationLink. A link names theOperationit leads to, aLinkParamvalue for each of that operation's parameters, itsBody, theServerit is called on, and its docs, provenance and extensions. ALinkValueis a kind beside the one field it selects, asValueis: aconstant, the exchange'surl,methodorstatus, arequestvalue as anInputPath(a parameter of the operation answered, or the request body and members within it), or aresponsevalue as aPropPath(a header, or the body and members within it). Members are named by ID, never by wire name.docs/ir/responses.md#linksgives the design and the alternatives it rejects.InputPathwith neither a parameter nor a segment is the request body whole, which a link'srequestBody: $request.bodypasses on;ir/input-path-empty, which refused it, is gone.operationId, or byoperationRefto the operation mounted at that position, else the one declared there and mounted once, using the same same-document reading a$refgets. Parameter keys resolve as<in>.<name>or a name one parameter carries. Body pointers walk the members of every content of the body. A link that does not resolve whole is kept verbatim underopenapi:links/<name>, so no consumer reads a link missing a value its source states. It draws anopenapi/invalid-linkwarning when it names nothing the document declares or breaks the expression grammar, and anopenapi/untyped-linkinfo when it is sound but reads what no link value spells, such as a member through an array index. Neither fails a compile. A$refin a links map is now followed, so thecomponents/linksentry it names is no longer also kept on the document.pass.Validateholds eachLinkParam.Paramto the link's target and each request value to the operation answered (pass/param-binding-mismatch).irverifyholds a link value's field to its kind (ir/unknown-link-value-kind,ir/link-value-missing-payload,ir/link-value-stray-payload).operation-links(Smithy@referencesand Protobufresource_referenceare marked as references to a resource, a different fact). Correlation keys reach a server only a link names.docs/compilers/openapi.md#linksis rewritten as the lowering rule.Off the golden spec: it declares no response link (
jq '[paths(objects) | select(.[-1]=="links" and .[-3]=="responses")] | length'→ 0 on api-schemas @319dc756). Of 1,840 OpenAPI 3 specs in a real-world corpus, 2 declare links, 5 in all, each targeting byoperationIdand reading$response.body#/….Closes #669
Test plan
go test ./ir/... ./compilers/... ./pass/... ./engine/... ./internal/... ./cmd/...,make vetandgolangci-lint run ./...pass; every touched package is at 100% statement coverage on its own tests.testdata/conformance/openapi/response-links.yamlis rewritten to reach every value kind, a success response, an error case and a default, a$ref'd link, a server, a qualified key, a case-insensitive header and a link kept verbatim. Each link is declared before the operation it leads to, the order that would lose it if links resolved as their response lowered. The census shows it witnesses every new fact, andInputPath.param,InputPath.segments,PropPath.inandPropPath.segments, which no spec had set before.invalid-linkback at error severity, against the findings table; three plants each against theirverifyandpasschecks.operation.TestLinks_AFindingKeepsTheLinkWholeholds every invalid and untyped form the rule lists to the same outcome: no link, the link verbatim under its own key, one finding at its entry, and the severity.e2e.TestUnpreservable_AnnouncementNeverOutrunsTheEntrycovers a link whose verbatim form cannot be kept. The finding then says nothing was kept, and a link that lowers converts no verbatim form at all.mainonly in links:extensions-x,yaml-merge-keysandunreferenced-componentsmove fromopenapi:linkstolinks.go run ./cmd/morphic-harnesspasses on the fixture and on a probe of every resolved form.invalid-linkwarning and lowers its other three links. Its exit status and error set matchmain's, two unrelated invalid references.Breaking
IRVersionmoves to 0.35.0. A consumer pinned to 0.34.0 refuses a document with a link, and finds noopenapi:linksentry where it read one.