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

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

Closed
DavidBiesack opened this issue Oct 28, 2014 · 71 comments
Labels
Moved to Moonwalk Issues that can be closed or migrated as being addressed in Moonwalk

Comments

@DavidBiesack
Copy link

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)

@maxlinc
Copy link
Contributor

maxlinc commented Mar 4, 2015

Real world example - OpenStack Server Actions. I've been trying to convert OpenStack WADL to Swagger and these operations have been a sticking point. There's no real way to describe them right now, because the Swagger doesn't allow path (which is what the WADL uses), nor does it allow "oneOf" or "anyOf" schemas. So I don't see any possible way to describe these services right now.

On the other hand, I want Swagger to define an API, not just describe it. That's the whole reason the working group decided to avoid some of these features in the first place - to avoid ambiguity. If this is going to be done it needs to be done in a way that works for:

  • Code generators (like swagger-codegen)
  • Stub servers
  • Testing tools

It's basically a routing problem: given an HTTP request you should know what operation is being called, and this makes it ambiguous.

@szhem
Copy link

szhem commented Apr 2, 2015

+100!

Recently we had to change our api because were unable to define multiple post methods per path. So api became uglier in favour of its documentation.

@earth2marsh
Copy link
Member

This is a feature of Swagger's opinion on APIs. For better or worse, it presumes that the signature of an operation is the resource path pattern plus the HTTP verb.

To change this would require an alternate mechanism for defining the signature. If we do pick this up in Swagger.next, then let's consider an overloading of a method with some sort of signature property. I'm not sure that this is something that it SHOULD do, but that is how I would tackle it. (and I agree with what @maxlinc said above)

@chrisregnier
Copy link

I recently came across the same problem and would love to have the ability to define the API and associate the same path with multiple endpoints that are still unique.

So I'd like to suggest that endpoints are not defined by their paths, but by a user defined key name. More than likely it'll be a domain specific operation name (eg: 'addBookByJson','addBookByBinary' may both have the same path but different endpoints/consumes). Then each endpoint would have a path associated with it instead of 'being' the path directly. Ambiguity is still easy to check based on an equality function of all defined endpoints (which could then be based on implementing platform which might be more applicable rather than defining the equality relation in the swagger API).

There are so many ways I could define an endpoint based on an HTTP request. And if swagger is to be a general tool for documentation, then I would much rather see swagger show its opinions in the form of defaults (or the easy path), NOT by limiting functionality when I need to go outside of the norm to support others or implement new ideas!

@theage
Copy link

theage commented May 5, 2015

+1

@eli-jordan
Copy link

+1
This would make the tool much more fliexible. I totally agree with @earth2marsh on this point. Allow the user decide what properties of an http request determine its signature.

@haidaraM
Copy link

+1

2 similar comments
@fabriciorissetto
Copy link

+1

@eirnym
Copy link

eirnym commented Aug 1, 2015

+1

@who
Copy link

who commented Aug 5, 2015

+1

This would be really fantastic:

{
    "in": "body",
    "name": "abstractThing",
    "schema": {
        "oneOf": [
            {
                "$ref": "#/definitions/ConcreteThing"
            },
            {
                "$ref": "#/definitions/ConcreteOtherThing"
            }
        ]
    }
}

@JosiasStraesser
Copy link

+1

@achingbrain
Copy link

+1

Recently we had to change our api because were unable to define multiple post methods per path. So api became uglier in favour of its documentation.

Feels a bit like the tail wagging the dog.

@Joyce-Stack
Copy link

+1

2 similar comments
@tom-squires
Copy link

+1

@jenarros
Copy link

+1

@DavidBiesack
Copy link
Author

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.

photon-gerrit pushed a commit to vmware-archive/xenon that referenced this issue Mar 16, 2016
A user can start a com.vmware.xenon.swagger.SwaggerDescriptorService
on a host and a swagger 2.0 description is served by default on /discovery/swagger.

* add new xenon-swagger module containing a single stateless service
* add enumValues to PropertyDescription describing an ENUM
* add kind to PropertyDescription describing a PODO
* add latest release of Swagger-UI as custom ui of swagger
* documentKind is used to refer to swagger Models
* fix bug in ServiceDocumentDescription.Builder that assigns a kind of
  java:lang:Number to Number-typed fields (like in NumericRange)

TODOs:
* read swagger annotations to provide more details and docs: currently
  not possible withoug peeking into the service classes
* figure out if a service support PATCH/OPTIONS/POST...: not possible
  without peeking into the service class and looking for overriden handler
  methods
* Swagger 2.0 cannot fully describe a service interface, track this:
   OAI/OpenAPI-Specification#182

Change-Id: I9d59dd63187c769994fe8b8ce3143e119dc3cdde
@gavincornwell
Copy link

+1

@webron
Copy link
Member

webron commented Mar 27, 2016

Parent: #574, #586.

@jtaylor10
Copy link

jtaylor10 commented May 3, 2016

Another possible format for modelling this, not perfect though either. It comes out of the REST principle that you use the verb. Now REST over HTTP forces verb to be GET,POST,etc and they suggest you use an _method parameter or some override parameter to POST operations to indicate your real verb. So with that in mind we could define a paramter with a special type or mark it someway as your verb-override parameter for the specific end point and then change the verb to what you want.

Would look something like this:

paths:
   /some/path
      parameters:
           _method:
               type: string
               verb-override: true
      post:
           ....
      copy:
           ....
      merge:
           ....

With this, anything that falls outside of the standard HTTP verbs are modelled as POST /some/path?_method=copy. It could also then be used for those servers that only support PATCH or other standard HTTP but not as well supported verbs through a method override.

@juan-quiver
Copy link

+1

@bkeeler-redox
Copy link

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

@fabiocastilhos
Copy link

+1

@Avkashhirpara
Copy link

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
Copy link

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
Copy link

dahei commented Feb 2, 2021

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
Copy link

saiya commented Aug 25, 2021

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
Copy link
Contributor

hkosova commented Aug 25, 2021

@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
Copy link

saiya commented Aug 25, 2021

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
Copy link

btodts commented Sep 24, 2021

+1

1 similar comment
@vellala2000
Copy link

+1

@jon-jon-jon
Copy link

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
Copy link
Author

@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
Copy link
Member

"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
Copy link

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
Copy link

0mjs commented Mar 18, 2022

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 handrews added the Moved to Moonwalk Issues that can be closed or migrated as being addressed in Moonwalk label Jan 24, 2024
@handrews
Copy link
Member

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
Copy link

eirnym commented Jan 26, 2024

@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
Copy link
Member

@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
Copy link

eirnym commented Jan 29, 2024

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
Copy link
Member

@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:

  • 3.1 was really a major release, and in fact quite a few people wanted to call it 4.0, but the marketing faction won out and we ditched semver and called it 3.1 on the grounds that, technically, the breakage was just that we replaced type: X, nullable: true with type: [X, "null"]. Which was true. But to really add full JSON Schema support was a huge effort, and I think no one person involved quite understood the entire impact at the time.
  • We've never actually published a minor release, so there's no trust (or, at this point, even expectation) that we'll use minor releases to meet the short-to-medium term needs of the community by releasing compatible improvements that tooling vendors can add to their existing code-bases without much effort.
  • We've stopped even publishing patch releases - 3.0.3 was published before 3.1.0. There's a ridiculous number of issues filed that are basically just asking for clarifications that could have been regularly released as patch releases that would only make the lives of tooling vendors easier by removing guesswork in certain areas of the spec.
  • We've created other places where we can address some things, like the huge demand for way more examples than would fit in the spec, but have not been able to promote or expand it as much as I think we need to.
  • The issue backlog became absurd. I think we had over 600 issues open before Lorna and I started closing things out and/or moving them over to Moonwalk. Questions were left unanswered for years, there was no indication of which things might be worked on or when anything might reach actual users. There are also too many channels and not enough attention - I just recently found out that discussions are also enabled on this repo.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Moved to Moonwalk Issues that can be closed or migrated as being addressed in Moonwalk
Projects
None yet
Development

No branches or pull requests