-
Notifications
You must be signed in to change notification settings - Fork 69
Redundancy between Arazzo and OAS endpoint definitions #365
-
I am curious about the redundancy that exists between an Arazzo document and the open api spec that it points to.
The OAPI spec already has the definition for the endpoints.
Why does Arazzo need to re-define said endpoints, why not just have a pointer to the OAPI spec and let the system read the definition directly from the OAPI spec?
Here's an example of an Arazzo doc from source:
summary: Login User
description: This workflow lays out the steps to login a user
inputs:
type: object
properties:
username:
type: string
password:
type: string
steps:
- stepId: loginStep
description: This step demonstrates the user login step
operationId: loginUser
parameters:
# parameters to inject into the loginUser operation (parameter name must be resolvable at the referenced operation and the value is determined using {expression} syntax)
- name: username
in: query
value: $inputs.username
- name: password
in: query
value: $inputs.password
successCriteria:
# assertions to determine step was successful
- condition: $statusCode == 200
outputs:
# outputs from this step
tokenExpires: $response.header.X-Expires-After
rateLimit: $response.header.X-Rate-Limit
outputs:
tokenExpires: $steps.loginStep.outputs.tokenExpires
In this case, the parameters section is already defined in the OAPI Spec and this step(loginUser) is already a pointer to the OAPISPec. All we need to do is to follow the pointer, find the operation definition in the OAPI SPec and figure out what needs to be sent and how in the http request. Why was it determined that this double definition is necessary? Thank you
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 5 comments 3 replies
-
It's not redundant.. just a little confusing probably. You're indicating WHICH of the defined operation parameters you are passing values in to FROM the inputs.
- name: username
in: query
value: $inputs.username
The operation (loginUser) defines username as a parameter. You are telling Arazzo that you want to pass to that operation the inputs FROM $inputs.username at that step.
If you think of this from a code generation perspective, the workflow becomes say, a single function.. the steps are then coded as a sequence of requests in that one function:
// Workflow ID loginUser
func loginUser(username, password: string) {
// step 1 (only one defined in this example.. but could be more)
err,resp := loginStep(username, password)
... more steps here if there were more defined ...
}
// Step 1 function
// Operation ref - loginUser
// loginUser defined with two parameters in its description (username and password as well)
// code generator tool sets up variables (elsewhere) for OpenAPI server info, etc
func loginStep(username, password: string) error, resp {
err, resp := httpCall(http.POST, loginAPIUrl, username, password)
return err, resp
}
This is just an example of what it might turn in to. If there were more steps in that workfow, they would be INSIDE The { } of that function. If you had a 2nd step that took in a parameter like userId that was returned from the first step, there might be some code to pull the userId from the resp object, then pass it on to the 2nd step.
So just to double up on the explanation.. the "outer" function receives input variables (username and password). Those are defined in the inputs: section. The step uses the $inputs... to access those. The step is setting it up to pass the $inputs.Username to the operations username property. as per the psuedo loginStep() function, that would match the step and as I commented there some of the things like the server it would make the API call to would be provided during code generation as well. Just left some of that out for brevity.
Make sense?
Beta Was this translation helpful? Give feedback.
All reactions
-
Thanks Kevin,
Makes sense overall. As you point out, it is clear that we need a template to hydrate with the inputs expected by the operations defined in the open api spec document. We also need a document to describe the sequence in which these operations will be called.
I was referring exclusively to the definitions of the inputs (e.g. parameters). In your example, let's look at this:
in: query
The fact that this parameter is to be submitted in the query can be obtained from the open api spec. My concern with two separate locations where the same above fact is available is that there is a possibility that in the arazzo document it is in: query while in the open api spec it is in: header. In other words, if the arazzo doc author made a mistake we now have an inconsistency.
If we say that arazzo overrides open api spec, this weakens the oapi spec which should always be the single source of truth. Alternatively, if we say that the oapi spec overrides arazzo, then we have a typo in arazzo and when an engineer is troibleshooting the code generators this will be a source of confusion and thus added maintenance.
What do you think is the right approach to explain this to an engineer that is new to this pattern? Should we just need to let the engineer know that we need to make sure that both arazzo and oapi spec should be the same to prevent any confusion?
Beta Was this translation helpful? Give feedback.
All reactions
-
So you bring up a valid potential confusion point. I agree that in: should refer to the operation provided. The only thing I can think of now off the top of my head when we came up with this is possible ambiguity situation where there could be the same name in two or more IN points? E.g. maybe username is in header AND in query? So this directly specifies it? Not sure that that is even possible now that I think about it, dont know off the top of my head if OpenAPI supports that, I dont see why it couldn't be allowed. A quick AI search returns this (if it's wrong I apologize.. I tend to use AI to find things more than google these days and we all know AI can get stuff wrong). The example shows the use of session_id in both a cookie and query:
paths:
/user-info:
get:
summary: Get user information
parameters:
- name: session_id
in: query
description: Session ID passed as a query parameter.
required: false
schema:
type: string
- name: X-Session-ID
in: header
description: Session ID passed in a custom header.
required: false
schema:
type: string
- name: session_id
in: cookie
description: Session ID passed as a cookie.
required: false
schema:
type: string
responses:
'200':
description: OK
Funny enough ChatGPT gives a similar example:
Yes — in OpenAPI you can technically define multiple parameters with the same name, but only if their in differs.
The spec says:
Parameter uniqueness is defined by the combination of name + in.
So you can have:
parameters:
- name: sessionId
in: query
schema: { type: string }
- name: sessionId
in: header
schema: { type: string }
- name: sessionId
in: cookie
schema: { type: string }
and that is valid OpenAPI 3.x.
Each is treated as a separate parameter, even though they share the same key string (sessionId).
I'll have to defer to @frankkilcommins if he recall is this is why we added that or not. It was some time back and clearly our documentation doesn't explain that. So I would say IF this is why, we should put in a PR to update the documentation around parameters to explain this.
Beta Was this translation helpful? Give feedback.
All reactions
-
Ok, this makes sense. At least, we can say that the arazzo parameter definition is used to disambiguate/select the right parameter in the case when the oapi spec offers multiple versions of the parameter with the same name but with a different delivery types.
It still would be great to have @frankkilcommins 's feedback in case there are other reasons for which this was added...
Beta Was this translation helpful? Give feedback.
All reactions
-
Hi @zepedaherbey and @kevinduffey,
Firstly, apologies in my delay in getting back to this discussion ping.
I can confirm the reason we had to be explicit with the in parameter property was to ensure that we can have explicit mapping given that it's the combination of name and in that defines parameter uniqueness in OpenAPI. From the OpenAPI Specification:
A unique parameter is defined by a combination of a name and location.
There could be a discussion to be had for future versions of Arazzo to relax the in property to be optional in all cases, and allow an author to leverage it only for scenarios where there are parameter name conflicts in the underlying spec.
Beta Was this translation helpful? Give feedback.
All reactions
-
@zepedaherbey I'm going to slightly tweak the title of this discussion because as it is it (ironically) appears to be redundant with some other discussions. If this bothers you in any way, please switch it back, or if GitHub won't let you, please comment and I will fix it.
Beta Was this translation helpful? Give feedback.
All reactions
This comment was marked as off-topic.
This comment was marked as off-topic.
-
This appears to be off-topic (Graph Learner? Butler? gRPC? Elevator Mechanic?) so I am marking it as such.
Beta Was this translation helpful? Give feedback.