OAI / OpenAPI-Specification

The OpenAPI Specification Repository
https://openapis.org
Apache License 2.0
28.52k stars 9.05k forks source link

Support an operation to have multiple specifications per path (e.g. multiple POST operation per path) #182

Closed DavidBiesack closed 5 months ago

DavidBiesack commented 9 years ago

with POST actions especially, there can be several unrelated operations that would be better presented to the user as separate POST actions in swagger-ui because they do very different things to the same resource. Each of these different operations should have a unique description string and unique media types to select from and other unique parameters (such as query parameters), and thus have separate presentation in swagger-ui. By simply allowing only just schema overloading, there is no way for me to constrain query parameter A to be used with just media type X, and query parameter B to be used with media type Y. These are implemented as two independent operations and it would be nice to be able to model them independently in Swagger.

So I think a proposal i saw elsewhere to annotate the operations themselves (to provide unique keys in the JSON object) would actually be more flexible than other workarounds to the fact that Swagger only allows one POST per path when a REST API may in fact support many independent POST calls.

i.e. something like

paths:
 /a/b/c:
   get: ....
   put: ...
   post.optimize: spec for the optimize operation
   post.merge: spec for the merge operation

(Initially discussed in #146)

DavidBiesack commented 4 years ago

It goes beyond this, @whitlockjc - i.e. not just input/output schemas, but extends to separate sets of query/header parameters (in addition to body params). At present, even tho OpenAPI 3.x lets you "partition" schemas based on media types, one must bundle all header/query parameters together with the one operation. We really want to be able let other signature items (not just path pattern + verb) to be used to "identify" or route a request and have separate operations for each.

bkeeler-redox commented 3 years ago

Wow, this is a big gap in OpenAPI, so it's quite surprising to see how long this discussion has been ongoing.

fabiocastilhos commented 3 years ago

+1

Avkashhirpara commented 3 years ago

I am facing the same issue. I want to document same path with multiple parameters. I have checked anyOf,OneOf, options but the it doesn't server the purpose. Need any other identifier other then Http methods + path.

if there is any update or alternative on the same, It would be helpful.

travishaagen commented 3 years ago

The long-standing work-around is to add a unique URL-fragment to the end of the path. However, if your OpenAPI JSON description is being auto-generated by some library that doesn't support this use-case, then you either need to find a different library or write your own code to generate the OpenAPI JSON. You could also hand-write the OpenAPI JSON :)

dahei commented 3 years ago

What we are doing in the interim is to use #tag in the paths to differentiate these.

/a/b/c: post: /a/b/c#merge: post:

To visually "hide" this #tag path param you can also use an empty unicode character like "Hairspace" (U+200A) - see https://emptycharacter.com/ for examples.

saiya commented 2 years ago

I encountered same problem when I am writing OpenAPI spec of my OAuth2 server.

My workaround is to use path parameter like below (rather than #tag way):

{
  "paths": {
    "/oauth/{code_grant_endpoint}": {
      "post": {
        "operationId": "codeGrant",
        "parameters": [
          { "name": "code_grant_endpoint", "in": "path", "required": true, "schema": { "type": "string", "enum": [ "token" ] } },
          // .... and other parameters ...
        ],
        // ...
      },
     "/oauth/{client_cred_endpoint}": {
      "post": {
        "operationId": "clientCredentialsGrant",
        "parameters": [
          { "name": "client_cred_endpoint", "in": "path", "required": true, "schema": { "type": "string", "enum": [ "token" ] } },
          // .... and other parameters ...
        ],
        // ...
      },
      // ...
    },
}

In above example, I defined two operations "codeGrant" and "clientCredentialsGrant".

Path of both operations are exactly same: /oauth/token.

Because of the path parameters in definition ({code_grant_endpoint}, {client_cred_endpoint}), it does not conflict in OpenAPI definition. With using enum schema restriction, the path is not variable actually.

This way works without any modfication or postprocess in OpenAPI definition consumer side (e.g. openapi-generator).

hkosova commented 2 years ago

@saiya

Because of the path parameters in definition ({code_grant_endpoint}, {client_cred_endpoint}), it does not conflict in OpenAPI definition.

Actually the /oauth/{code_grant_endpoint} and /oauth/{client_cred_endpoint} paths are considered identical and therefore invalid.

From the spec (emphasis mine):

Templated paths with the same hierarchy but different templated names MUST NOT exist as they are identical.

saiya commented 2 years ago

paths are considered identical and therefore invalid.

Oh... thank you for comment.

It seems work in my case, but now I understand it is implementation specific behavior...

Sad for the restriction.

btodts commented 2 years ago

+1

vellala2000 commented 2 years ago

+1

jon-jon-jon commented 2 years ago

My 5 cent and solution: Instead of defining your query parameters (normal way), just hard-code your query id in the URL and set its value as "path" type. It is not perfect but it's working fine =) You still use component, description and other Swagger's functionality to be more friendly ;)

 /myUrl/myRoute/?queryPara1={query1}?queryPara2={query2}:
    get:
      parameters:
        - in: path
          name: query1
          schema:
            type: string
        - in: path
          name: query2
          schema:
            type: string

  /myUrl/myRoute/?queryPara3={query3}?queryPara4={query4}:
    get:
      parameters:
        - in: path
          name: query3
          schema:
            type: string
        - in: path
          name: query4
          schema:
            type: string
DavidBiesack commented 2 years ago

@jon-jon-jon interesting... OpenAPI does not seem to restrict the value of a path to be just relative-part of RFC 7230, at least not formally ("A relative path to an individual endpoint") , i.e. the OpenAPI path does not specifically exclude the [ "?" query ] [ "#" fragment ] portion. That's up to the OAS maintainers to address. So be wary; they may impose such a restriction in the future. Also keep in mind that tools that consume this OpenAPI (SDK code gen, etc.) would likely get confused by this, as these parameters are not really path parameters but query parameters, and libraries to generate paths or parse paths may not work. Also, query parameter order does not matter (except for array params), so your API won't support valid calls with different order /myUrl/myRoute/?queryPara4={query4}&queryPara3={query3} which would be expected with properly defined query parameters. Finally, with OAS, parameters with in: path must also be required: true, so if "it's working fine", that is only in the sense that perhaps your code supports it, but the actual OAS document would be invalid.

karenetheridge commented 2 years ago

"it's working fine" suggests that the specification needs to be more clear here (https://spec.openapis.org/oas/v3.1.0#patterned-fields). In order to match the request's path against all the path-item properties, we need to clearly define just what the "path" is. It can't be true that the path both contains and DOESN'T contain the query parameters.

Regarding fragments, I think we can be a little more certain, because fragments are not supposed to be handled by servers at all - it's purely a client-side thing. I would certainly regard any attempt by openapi tooling to specify or check the fragment to be an error.

peteraritchie commented 2 years ago

Regarding fragments, I think we can be a little more certain, because fragments are not supposed to be handled by servers at all - it's purely a client-side thing. I would certainly regard any attempt by openapi tooling to specify or check the fragment to be an error.

User agents aren't even expected to send the fragment in the request to the server.

0mjs commented 2 years ago

What we are doing in the interim is to use #tag in the paths to differentiate these.

/a/b/c: post: /a/b/c#merge: post:

We have customized Swagger UI to hide these tags and remove them from the Try It/curl actions

Each of these post operations can have their own parameters/produces/consumes.

I realize that almost none of the existing Swagger ecosystem will work with this, but we need to move forward, and this is the path we're taking. It was the easiest and lowest impact change to Swagger UI and the swagger documents, and it works fairly well. Our expectation is to be able to transform our use to wherever Swagger.Next goes.

This was some time ago, but this thread has been a good source of truth, is there chance you could tell me how you removed the tag from the 'Try it out' interface?

handrews commented 5 months ago

The Moonwalk (OAS 4) proposal has affirmed a principle of operation signatures which will address this use case 🎉

As this cannot be done within the 3.x line, please follow the Moonwalk repo discussions for further developments.

eirnym commented 5 months ago

@handrews with OAS 3.1 failed adoption because of Swagger libraries for more than 3 years, I wonder how long it'll take it to adopt OAS 4

handrews commented 5 months ago

@eirnym Swagger is not the only product in the OpenAPI ecosystem. Other major tooling vendors started adding 3.1 support as early as 2 months after 3.1.0's publication. Swagger's late support just ensured that new OAS users looking for modern technologies chose other vendors. From an ecosystem point of view, it's actually better to have multiple competing major vendors rather than a single dominant one.

Adoption of 3.1 continues to grow, including in larger organizations less likely to be on the bleeding edge.

OAS 3.0.0 was released in 2017. When we published OAS 3.1.0 four years later, OAS 3 still had not really supplanted OAS 2. But by now it has. These things always take more time than anyone would like.

That said, we are already looking at ways to make Moonwalk more adoptable.

eirnym commented 4 months ago

I totally agree that swagger is only one of tools to be used for OAS. However, I've observed in few enterprise companies that "We can't use OAS 3.1 because of Swagger and we don't accept any other third party tool".

What do you propose for developers to say teams responsible for API in a company to increase popularity of Moonwalk (as a next OAS generation)?

handrews commented 4 months ago

@eirnym This is a complex topic that probably belongs somewhere other than comments on a random closed/moved issue. But I'll take a shot at it here- this is something that interests me very much, along with ensuring that we have a series of small but useful 3.x releases to both deliver features for users and tooling vendors who are just now getting to 3.1 and make the migration to Moonwalk more smooth when it becomes possible.

My personal view (and while as a maintainer I have some write privileges in this repo, I am not in any way speaking as a representative of the project's official position), is that the OpenAPI Initiative has lost the trust of at least significant part of its community. This due to several things, all of which are understandable individually but have added up to a difficult state of affairs:

I think a lot of this had to do with the pandemic. Several people, including myself, became unable to focus on these projects. There have also been some weird things that have been logistically challenging due to unavailability of people who worked on those things in the past, and lack of understanding of how and why things are the way they are. The broken links on the rendered Markdown in GitHub would be the most glaring example. There were also people who felt that with 3.1.0 out, there shouldn't be more 3.0.x releases. And there was no point in doing 3.1.1 when 3.1.0 wasn't implemented yet.

Last year things were still moving slowly and sporadically as I got involved again (I was moving pretty slowly and sporadically, too). But there is a huge burst of energy from new and returning people as we kick off 2024, and we are actively working to put our house in order.

This is starting with the expansion of the Technical Steering Committee so that we have a full roster of active TSC members. Once we have our governance revived, we'll be able to make more headway on that list of process issues.

We've changed the agenda for the weekly Thursday calls to block out time for process and governance at the start of the meeting to make sure we make progress. We've also split Moonwalk out into a Tuesday meeting, leaving the Thursday meeting to focus on 3.x plus governance (we've also considered splitting out the governance work, but at the moment it feels like that's not needed and would be too many meetings). We're also talking about finding some meeting times that suit more time zones and maybe rotating the time slot a bit.

Also in that New Year's Cleaning meta-issue, you'll see issues about defining our minor and patch release strategies for 3.x(.y). You'll see that I've proposed an overall philosophy of "smoothing the path to Moonwalk." I'm not the only person advocating for 3.x – I think @lornajane first brought up the need to keep our focus on it and not just rush off to Moonwalk, and she has continued to advocate for it in the calls.

I think we have to rebuild trust by clarifying as much as we can for vendors and users in 3.0.x and 3.1.x patch releases (since the majority of users are still on 3.0), and by releasing small, incremental, valuable 3.x releases so that end-users see benefits and signs that their needs are being listened to well before Moonwalk support becomes widespread. Where possible, we should backport Moonwalk features to 3.x to increase convergence (like Python 2.x and 3.x releases when they overlapped). I'm currently going a bit overboard adding new labels to the remaining backlog to figure out the patterns in requests that could help us define coherent and valuable 3.x(.y) releases.

I have some other ideas about making the path to moonwalk less of a hug gap to jump across, but I haven't floated them with anyone yet and they're a little too half-baked to write about publicly at this point. But I plan to be in both the Tuesday 4.x and Thursday 3.x calls on a regular basis, pushing for something that is not just "release a de-facto major version that requires a complete architectural re-write every 3 to 4 years."

Moonwalk is exciting, but my personal opinion is that we need to tend to the 3.x users, and show that it is worthwhile to move to 3.1 because they will be rewarded with a steady stream of incremental 3.x releases that will make the transition to Moonwalk as smooth as possible when the time comes.