OAI / OpenAPI-Specification

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

Support for multiple request/response models based on headers #146

Closed jahlborn closed 7 years ago

jahlborn commented 10 years ago

I guess this is a 2.0+ request as 2.0 is already "released".

Basically, there is currently no way to express a single path which may have different request/response formats based on additional criteria, namely headers. two possible scenarios:

  1. An API which supports both xml and json, but the models for each format are different enough that they cannot be expressed via a single json schema w/ xml annotations (even if the xml format is expressible using the xml annotations).
  2. An API which supports versioning the representation via the media-type. e.g. application/my+v1 returns a json model which is sufficiently different from the application/my+v2 model such that i cannot describe them both using one unified model.

If supporting different responses for arbitrary headers is too big of a change, just supporting multiple media-types (for request and response) would be sufficient for the cases described here.

mohsen1 commented 10 years ago

Let's say you have your content type param as a path param. Like what Reddit is doing. See this. Then it will be represented like this:

swagger: 2
info:
  version: "1.0.0"
  title: reddit
paths:
  /r/{sub}:
    get:
      responses:
        200:
          description:  HTML
    post:
      responses:
        500:
          description: You can't
  /r/{sub}.json:
    get:
      responses:
        200:
          description:  JSON

Now imagine the same API wants to use content-header for the response type. We can have another level of abstraction for different content types

swagger: 2
info:
  version: "1.0.0"
  title: reddit
paths:
  /r/{sub}:
    get:
      content-types:
        XML:
          paramaeters:
            -
              name: type
              in: header
              description: Content-Type
          responses:
            200:
              description:  HTML
        JSON:
          paramaeters:
            -
              name: type
              in: header
              description: Content-Type
          responses:
            200:
              description:  JSON
    post:
      responses:
        500:
          description: You can't

This is just a raw idea. I don't know if it's even practical

webron commented 10 years ago

The first sample @mohsen1 presented is something that can be done today as the content type is being controlled by the path and there's no issue representing it.

The second sample is closer to what @jahlborn expected, I assume.

I'm not convinced how common this use case is (not saying it doesn't exist though, of course). I'd hope to see a solution that doesn't complicate the spec for users who don't need this feature to avoid complication as much as possible. We're trying to create a spec that's simple enough for people to use without having to deal with complexity if they don't need it. I suspect we'll manage to find an elegant solution for this one though.

jahlborn commented 10 years ago

If we were only looking to support different schemas based on media-type, then the current spec isn't very far off. you can already provide a list of media-types in produces and consumes. if you allowed multiple "in:body" request parameters with an additional media-type parameter, then that would cover the requests. the harder part is the responses. ideally, you would want "schema" to instead be "schemas", which is a map from media-type to the Schema Object. This overall design assumes that everything else about the operation is the same other than different formats based on media types. the second example from @mohsen1 is a much bigger change which allows much greater differences in the operation for each media type.

mohsen1 commented 10 years ago

I like the schemas idea. That way if an endpoint returning different schemas based on any parameters we can describe it. This is how it's going to look like. Note that there is no mapping from the parameter to the schemas. Is that a problem?

swagger: 2
info:
  version: "1.0.0"
  title: reddit
paths:
  /r/{sub}:
    get:
      parameters:
        - name: Content-Type
          in: header

      responses:
        200:
          description:  HTML or JSON based content header
          schemas: 
            -
              type: string
              description: XML String
            -
              type: object
              propertirs:
                name: 
                  type: string
    post:
      responses:
        500:
          description: You can't
fehguy commented 10 years ago

per design, we don't overload response type definitions for the same response code.

jahlborn commented 10 years ago

@fehguy - i understand that this is currently the case, which is why this issue is tagged as "swagger.next". or are you saying this will never be the case, in which case this issue is moot?

fehguy commented 10 years ago

sorry, carry on. I suggest proposing anything with that label--there was a fair amount of discussion in the workgroup about avoiding overloading response types.

jahlborn commented 10 years ago

@fehguy - so going back and reviewing that thread, i agree with most of the arguments against overloading in that thread. specifically, you said "Yes, swagger has always had a deterministic response type per operation (combination of http request method + path + http response code)". later you mentioned the proposal to include all the query params, and that you felt that was "ugly". i largely agree with this. while my initial description above does indicate choosing model based on any header, that probably falls into a similar category as all the query parameters. I would agree that the REST ideals aim for the url and http method defining the resource. however, i would argue that the media type is still an important factor. a given resource can have many formats, and that is controlled by the media-type related headers. So, i still think there should be (at least) the ability to specify multiple models for input and output based on media-types.

DavidBiesack commented 10 years ago

For another data point, I will mention that our APIs currently allow overloading of path/methods and use the Content-Type and Accept headers to indicate the representation of the request and response bodies. Thus, a GET or PUT or POST to /a/b/c may allow any of n different, discrete media types (each are different representation of the resource). In particular, we use different media types to distinguish different JSON bodies, i.e.

GET /a/b/c 
Content-Typet:application/vnd.sas.myresource+json 

GET /a/b/c 
Content-Typet:application/vnd.sas.alternate+json 

GET /a/b/c 
Accept:application/pdf 

POST /a/b/c 
Accept:text/html

Of note, we do not rely on just a "file extension" such as .json to determine a response type, as we allow for multiple different application/vnd.sas.*+json media types for some resources.

So this ability (and the association in swagger-ui of a media type with a schema) is very important to our potential use of Swagger.

mohsen1 commented 10 years ago

This feature certainly is going to be part of next version of Swagger. There is no way to not break the standard and have operation overloading. I have another proposal for operation overloading syntax. Here it is:

swagger: '2.1'
info:
  version: "1.0.0"
  title: File
paths:
  /file:
    get:
      responses:
        200:
          description:  Return HTML by default
    get[application/vnd.sas.a+json]:
      responses:
        200:
          description:  Return JSON
    get[application/pdf]:
      response:
        200:
          description: Returns PDF

Advantages:

Disadvantages:

Them main conflict is that we are trying to model an API in a tree structure while operations and parameters can not have parent/child or sibling relationships

jahlborn commented 10 years ago

@mohsen1 from the comments made by @fehguy, i'm guessing that this would probably only ever be entertained for media-types, not for headers in general. i'm pretty much okay with that.

i think that putting the media-type with the method, though, opens up too much freedom for abusing the media-type. the current design of swagger works from the standpoint that a path and http method uniquely define a resource. by overloading at the http method level, you are allowing all the parameters, responses, etc. to be different based on the media-type. this would essentially make the argument that a resource is defined by path, http method, and media-type.

in contrast, my compromise proposal at this point, is that path and http method still uniquely define a resource, but that we should allow for different representations of that resource based on media-type (this is essentially what @DavidBiesack is asking for, and basically what the examples i gave in the original description describe). as i said above, i think that using a schemas property (both in responses and in the body parameter) would be a relatively small change from the current swagger spec which would allow for different representations of the same resource, but otherwise keep the rest of the resource's spec unchanged.

swagger: '2.1'
info:
  version: "1.0.0"
  title: File
paths:
  /file:
    get:
      responses:
        200:
          description:  Return HTML by default, also support JSON and PDF.
          schemas:
            default:
              ... html definition ...
            application/vnd.sas.a+json:
              ... json definition ...
            application/pdf:
              ... pdf definition ...
maxlinc commented 9 years ago

path and http method still uniquely define a resource, but that we should allow for different representations of that resource based on media-type

Definitely agree and think that overloaded schemas looks like a much easier spec change to deal with than overloaded methods.

DavidBiesack commented 9 years ago

However, 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
webron commented 9 years ago

@DavidBiesack - what you're talking about is different than intended by the original topic, and I'd recommend opening a separate thread for it. They may be solved in a similar manner, but providing support for one issue does not guarantee we'll provide support for another.

webron commented 9 years ago

@jahlborn - Do you have references to publicly available APIs that use the method of different mime types for different data representations? Of course, I'm not talking about the basic json vs xml but rather the one you were talking about.

DavidBiesack commented 9 years ago

@jahlborn GitHub's REST API lets you GET an issue in multiple formats; see GET /repos/:owner/:repo/issues/:number

and supports these representations of a GitHub issue:

See GitHub Media Types : Custom media types are used in the API to let consumers choose the format of the data they wish to receive. This is done by adding one or more of the following types to the Accept header when you make a request.

jahlborn commented 9 years ago

Thanks @DavidBiesack, the GitHub api is a great example.

webron commented 9 years ago

That example shows the versioning representation, but not the difference in modeling. Also one example, is not necessarily enough.

I'd love to see more examples, if you know of any.

The first reason, is to see that it is, in fact, a common practice. The second, is that with more examples we can end up having a definition that covers the use case properly with possible support for some edge cases.

There's no hurry in providing such samples as it'll be a while before this is actually implemented in the spec. Just feel free to point to additional APIs as you encounter them.

DavidBiesack commented 9 years ago

I disagree; the GitHub API clearly shows the resource representation difference. For a given resource, I can ask for the raw Markdown source; the clean text representation, html representation or full json representation of the same resource. See the Comment Body Properties section where each of these media types is defined.

Also for different representations, the Google Drive API provides an analogous mechanism (though query parameters, not via Accept headers) for the exportLinks. I.e. grab a spreadsheet resource out of Google Drive and it will contain media-type specific export links such as

"exportLinks": {
 "application/pdf”
    : "https://docs.google.com/feeds/download/spreadsheets/Export
       ?key=0AqGUPYh362KvdC1aMURSNVIyVnNYM2ZabVJCRi1GNnc
       &exportFormat=pdf",
 "application/x-vnd.oasis.opendocument.spreadsheet”
 : "https://docs.google.com/feeds/download/spreadsheets/Export
     ?key=0AqGUPYh362KvdC1aMURSNVIyVnNYM2ZabVJCRi1GNnc
      &exportFormat=ods",
 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet”
    : "https://docs.google.com/feeds/download/spreadsheets/Export
       ?key=0AqGUPYh362KvdC1aMURSNVIyVnNYM2ZabVJCRi1GNnc
       &exportFormat=xlsx"
   }

geoserver REST API supports multiple media types via Content-Type, for example POST /styles can consume a application/vnd.ogc.sld+xml or a application/json and GET /styles/{s} can return SLD, XML, JSON, or HTML.

webron commented 9 years ago

I'm referring to xml/json interoperability. We're not trying to describe how a response would look differently as a pdf vs xlsx. Github's API doesn't even support XML.

The geoserver documentation is unclear as to how the data formats are actually represented by the different content types.

DavidBiesack commented 9 years ago

@webron What do you mean by "xml/json interoperability"? Earlier you wrote

Do you have references to publicly available APIs that use the method of different mime types for different data representations? Of course, I'm not talking about the basic json vs xml but rather the one you were talking about.

so I was looking for exactly that - different data representations/mime types of the same resource.

webron commented 9 years ago

I'm referring to point 1 of the original post, @DavidBiesack. @jahlborn knows what I'm talking about as that's basically what started this issue in the first place.

Thank you for looking for samples though, I'm happy to see you're actively trying to pursue it.

jahlborn commented 9 years ago

Just did a quick google search, found https://xively.com/dev/docs/api/communicating/data_formats/ . their json and xml formats are similar, but i believe there are differences which would fall out of the current swagger support (particularly the response). it looks like they are constrained by an existing "standard" schema for the xml (which also kind of ties into one of my previous requests for first class xml schema support, Issue #54).

nickretallack commented 9 years ago

Here's what I want:

consumes:
  json: application/json
  protobuf: application/vnd.google.protobuf
produces:
  json: application/json
  protobuf: application/vnd.google.protobuf
paths:
  /things:
    post:
      operationId: post-things
      parameters:
        - name: json
          in: body
          description: JSON payload
          mime: json
          schema:
            type: array
            items:
              type: string
        - name: protobuf
          in: body
          description: Protobuf payload
          mime: protobuf
          schema:
            title: ThingPostMessageProtobuf
      responses:
        200:
          description: OK
          schemas:
            json:
              type: array
              items:
                type: boolean
            protobuf:
              title: ThingResponseMessageProtobuf
nickretallack commented 9 years ago

Or alternatively, to make the "schemas" thing consistent, and show off the other location of consumes/produces:

paths:
  /things:
    post:
      operationId: post-things
      consumes:
        json: application/json
        protobuf: application/vnd.google.protobuf
      produces:
        json: application/json
        protobuf: application/vnd.google.protobuf
      parameters:
        - name: data
          in: body
          description: Data
          schemas:
            json:
              type: array
              items:
                type: string
            protobuf:
              title: ThingPostMessageProtobuf
      responses:
        200:
          description: An array of products
          schemas:
            json:
              type: array
              items:
                type: boolean
            protobuf:
              title: ThingResponseMessageProtobuf
nickretallack commented 9 years ago

Here's another idea that's like my first example but more consistent about things. It allows you to nest all properties of a response under the applicable content type, in case you want to describe that content type. It's unfortunately less backward compatible though.


paths:
  /things:
    post:
      operationId: post-things
      consumes:
        json: application/json
        protobuf: application/vnd.google.protobuf
      produces:
        json: application/json
        protobuf: application/vnd.google.protobuf
      parameters:
        - name: json
          in: body
          description: JSON payload
          mime: json
          schema:
            type: array
            items:
              type: string
        - name: protobuf
          in: body
          description: Protobuf payload
          mime: protobuf
          schema:
            title: ThingPostMessageProtobuf
      responses:
        200:
          json:
            description: JSON response
            schema:
              type: array
              items:
                type: boolean
          protobuf:
            description: Protobuf response
            schema:
              title: ThingResponseMessageProtobuf
BigSocial commented 9 years ago

I just switched to Swagger 1.5M2 and half my documentation disappeared because of this. How is it possible this was missed in the Swagger 2.0 spec? For example, I have a resource to which you can POST JSON, a file in MULTIPART_FORM_DATA, or a raw file in APPLICATION_OCTET_STREAM. So now, not only can't I use Swagger Annotations to document my API, I apparently can't even use Swagger 2.0. Can someone please explain the rationale here, because it escapes me?

webron commented 9 years ago

@BigSocial - Swagger never supported this paradigm, it was not removed in Swagger 2.0.

Swagger does not attempt to cover all use cases, and we don't hide it. I honestly don't recall right now if this specific feature was requested in the workgroup we had for 2.0, and if it was why specifically it was rejected.

Swagger 2.0 was a huge revamp of the old version(s) and we couldn't possibly contain all features and requests. As you can see here, there's a healthy discussion with suggestions and we'd certainly take it under consideration for the next version of the spec.

BigSocial commented 9 years ago

@webron - Thanks very much for your quick reply.

First, I'd like to counter your assertion that Swagger never supported this. Using Swagger core 1.3.10 this code...

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;

@Path("/v1/test")
@Produces("text/plain")
@Api(value="test", description = "A test")
public class SomeResource {

   @ApiOperation(value = "Get Text")
   @GET
   public String doGetAsPlainText() {
       return "One";
   }

   @ApiOperation(value = "Get HTML")
   @GET
   @Produces("text/html")
   public String doGetAsHtml() {
       return "<html><body>One</body></html>";
   }
}

...produces this JSON...

{
apiVersion: "1.0.0",
swaggerVersion: "1.2",
basePath: "/api",
resourcePath: "/test",
produces: [
    "text/plain"
],
apis: [
    {
    path: "/v1/test",
    operations: [
        {
        method: "GET",
        summary: "Get Text",
        notes: "",
        type: "void",
        nickname: "doGetAsPlainText",
        parameters: [ ]
        },
        {
        method: "GET",
        summary: "Get HTML",
        notes: "",
        type: "void",
        nickname: "doGetAsHtml",
        produces: [
            "text/html"
        ],
        parameters: [ ]
        }
        ]
    }
    ]
}

...which produces this output from Swagger UI...

shot1

...so you can imagine my surprise when this functionality disappeared.

Second, and more importantly, while I understand there's no way Swagger can be all things to all people, let me refer you to the example code in section 3.1.3 of the Jersey 2.17 User Guide. Section 3 of this document describes what is essentially the heart of JAX-RS. The code below is some of the first a developer new to Jersey might see, and is replicated in any number of blog posts and books on Jersey.

@Path("/myResource")
@Produces("text/plain")
public class SomeResource {
    @GET
    public String doGetAsPlainText() {
        ...
    }

    @GET
    @Produces("text/html")
    public String doGetAsHtml() {
        ...
    }
}

As you see, this simple example early in the documentation for the JAX-RS reference implementation cannot be documented using Swagger 2.0. You may see this as a fringe feature of JAX-RS, but the Jersey team certainly doesn't and (I'd venture to say) nor do the many developers working on APIs that contain something other than the simplest JSON in/JSON out resources. Certainly the code above can be changed to use a single method, but doing so adds code, adds complexity and reduces readability. In my particular case, a single method that accepts JSON, MULTIPART_FORM_DATA, or APPLICATION_OCTET_STREAM would be very ugly if it worked at all, which I find unlikely.

By dropping support for this, Swagger 2.0 has essentially removed developer's ability to use this powerful, very visible and commonly used feature of JAX-RS, or forced them to find an alternate documentation method. I strongly urge you to consider returning support for this excellent, elegant coding paradigm to Swagger.

okigan commented 9 years ago

+1

webron commented 9 years ago

@BigSocial - what you showed in the 1.3.10 version is a bug - it is definitely not supported by the spec, and wasn't supported in 1.2

From the 1.2 spec - https://github.com/swagger-api/swagger-spec/blob/master/versions/1.2.md#523-operation-object - In the operations array, there MUST be only one Operation Object per method..

So this functionality wasn't dropped, it was never supported. Bug or no bug, it was not part of the spec.

As for JAX-RS allowing that functionality, that's true. Other frameworks allow this functionality as well. However, Swagger does not try or aim to cover 100% of the design options out there. It's an evolving spec and with each version we try to add more functionality, but we definitely don't try to cover it all.

That said, I believe this is a strong candidate for the next version of the spec. I cannot guarantee what will go in, be rejected, or postponed, but it definitely seems many users look for this functionality which to me make it a strong candidate.

We just need to find a proper way to describe it thinking of the ecosystem as a whole. Don't know if we've hit the right spot of it already.

BigSocial commented 9 years ago

@webron - Not sure if this should make me laugh or cry :smiley:

I appreciate your response. Just a couple points and then I'll leave this be for now.

  1. The fact that it worked and worked perfectly in 1.3 should say something to us about the flexibility of the old spec and web UI code even if unintended.
  2. By moving operations out of an array and into individual objects with fixed names you may have SPECed yourselves into a corner vis-à-vis elegantly supporting method overloading, unless you're willing to allow duplicates which is valid in JSON but probably wouldn't work in JavaScript and is certainly not how people think about JSON. IMO the old 'method' field was much more flexible and would make resolving this from the standpoint of the spec much easier. My solution would probably be to solve it at the method level anyway, even if you have to support something less than perfect like "get2", "get3", etc.

UPDATE: Now I think about it you could allow each method to be an object (the normal case) or an array. This would leave the spec backwards compatible but allow arbitrary overloading of any HTTP method.

Here's hoping this make it into the next round of the spec. Thanks!

webron commented 9 years ago

FWIW, it's not valid in JSON and any parser that reads that would use either the first or the last definition with the same keyword, overriding the rest.

The array vs. single object approach is interesting, thanks for the suggestion. Feel free to bring in more input if you have it, it's very much appreciated.

jahlborn commented 9 years ago

I don't want to beat this topic to death. I do want to address the "swagger does not try to aim to cover 100% ..." comment. This has come up a few times, and i fully support that ideal. However, i feel like that response is trying to frame this suggestion as some sort of "fringe" or "non-standard" need. I think that the concept of multiple media types for a resource is a "main stream" REST thing that swagger should clearly support. I was part of the 2.0 spec working group and the only reason i didn't push on this earlier was because i didn't realize it until too late (my company didn't actually try to implement the spec until it was finalized).

webron commented 9 years ago

It's not about being fringe or non-standard. Nobody really pushed for this requirement in the 2.0 work group, and as you say, you were there to witness that. Had people pushed for that more in the work group or prior to that, it would have probably gotten it. We can't possibly predict the requirements of all users, and this is exactly why we opened up the discussion in the work group. This is also why we created a dedicated repository for the spec to allow people to be more involved and affect the spec.

I can assure you that some requests are not going to make it into the spec, just like I can assure you that many others will. I've said before that that I believe this request is a strong candidate (among others). The gap is obvious and we need to fill it. I don't think there's an agreement on the structure though, and that's what we should concentrate on.

For any feature to be considered, I'd like to see a few things:

I'm definitely not saying all of the above is required for a proposal to be accepted, just that it can really help in the process.

The proposal is probably the most difficult part. It needs to be elegant (that is, easy to be written manually as well and not look like a blob of information), be reasonably easily supported by the tools of the ecosystem (editor, ui, codegen and of course other tools), and be able to supported by the 'major' development languages/frameworks out there. Try not to hold me to that definition and get the general idea here.

For this issue, I would love to see clearer suggestions for implementation. There have been a couple of suggestions but again, no feedback from most users in this thread (and out of it).

I really appreciate the contribution of everyone here and believe we can find the proper solution. It's definitely not impossible to do.

webron commented 9 years ago

Are we really going to continue back and forth on this? I didn't say it's a requirement for a proposal to be accepted. I said I'd like to see these things. And right after the bullets, I specifically said they are definitely not all required. There can even be one-line proposals that would be accepted.

And this is the spec community.

jahlborn commented 9 years ago

Sorry, my last comment wasn't super constructive. i just feel like there's been a lot of pushback on this feature request when it seems like a pretty straightforward need for REST APIs. your bullet point comment seemed to imply that the chances of acceptance are slim unless you meet all those criteria.

for your comments on implementation, you desire something which fits will with the current swagger ecosystem. frankly, i only use swagger from the perspective of generating it for our API server. My primary desire is to be able to describe the functionality that our server provides. i don't know much about how the rest of the swagger ecosystem works, or what would "work best". i would imagine that you have a much better handle on that. there are a number of proposals in this thread so far, which one looks the best to you?

webron commented 9 years ago

On the contrary, @jahlborn. The reason I push for it is because I believe this is one of the stronger candidates for the next version and would rather be able to present it as a ready-and-packaged solution instead of opening it for a long discussion. It's better if we can use the time prior to the official process so we can spend the official process to go through the possibly more controversial issues (keep in mind I'm in no position to guarantee anything regarding this feature request).

Completely understand your point regarding the ecosystem, and obviously not all users can see the whole effect, which is absolutely fine. I admit that personally I didn't dive into the suggestions that much and will set aside time to go over the stronger feature requests and provide my own input. For now, I just try to get the community involved and get feedback.

DavidBiesack commented 9 years ago

Thanks, @webron - I appreciate being open to addressing this issue in the next rev of Swagger.

My primary use case is overloading a method+path with different Content-Type or Accept header media types (body type parameter). Thus I would like:

  1. associate media types with schemas
  2. a means to "merge" overloaded methods into one in the UI. For example, I really don't want the UI presentation that @BigSocial posted above in comment 96417650 above that is I don't want GET /v1/test Get Text GET /v1/test Get HTML I want just one entry GET /v1/test Get resource X and use the accept/consumes UI selectors to choose the media types.

This merging does not have to be automatic, but I would like to have some way to link them so they are merged.

Note that this also means that selecting a media type for the request body would also (in the UI) choose a different schema and sample body. This is why I want to associate media types with schema.

That is, there is an abstract API that consists of method+path and there is a implementation that may consist of multiple controllers per method+path. (We use spring MVC which supports multiple controllers for a method+path. The different controllers can specify different consumes= or produces= media types and the Spring dispatcher handles this, calling the right controller based on the value of the Content-Type or Accept headers). I don't want such implementation decisions to affect the visible API presented in the Swagger UI or in static doc (HTML) we create with Swagger codegen. I want the consumer to see the abstract API.

Hope this helps.

webron commented 9 years ago

@DavidBiesack - Thanks! That's a very important input regarding the UI. As you said, it may be configurable, but very interesting.

dilipkrish commented 9 years ago

:+1: for what @DavidBiesack.

To add to it, I would think documenting other media types is not in scope for swagger, which in itself is one (at least I'd like to push for it to be one) :smiley: The reason I'd say its out of scope, is I don't think the swagger toolchain would be responsible for enforcing and validating these extensions. For e.g. we wouldn't try and describe or validate a html response for the text/html media type.

Now it might be a different thing if we do end up with json dialects masquerading as specialized media types e.g. application/vnd.customer+jsonor application/vnd.order+json etc. and we use json schema to document that; which I'd still argue is an implementation detail. I say that because there is an implicit shared understanding between a client and server when say an operation produces a response with the application/vnd.order+json media type. Media types are in itself self describing for e.g. standard media types like HAL, Collection+Json, Siren etc. and I don't think swagger specification should try to address describing a media type.

BigSocial commented 9 years ago

@DavidBiesack I agree that the ultimate UI product should have as much or as little merging as necessary in order to create the best user experience. What I'm against is having the core/spec try to generate/describe this merge. Many of spec change suggestions above solve point use cases but don't address the fact that trying to coerce all variations and combinations of produces, consumes, parameters, etc. into the existing Operation Object will likely generate a combinatorial explosion, will certainly be ugly, and, I'm guessing, struggle to maintain backwards compatibility with the 2.0 spec.

My recommendation is to allow the fields in the Path Item Object that currently take Operation Object (get, put, etc.) to also allow an array of Operation Objects. I'll defend this notion with the following points.

  1. It's an extremely easy to understand extension of the 2.0 spec.
  2. It's structurally one for one with JAX-RS annotated methods and would lose none of their semantic clarity.
  3. It solves a huge swath of the use cases (not formalized yet, sorry @webron) we've been discussing.
  4. Being so high level and simple it's likely to be easier to implement for developers than if each produces, consumes, parameter, etc. for a single Operation Object has to be annotated with which of the other produces, consumes, parameter, etc. it applies to. In my experience, changing high level, important constructs (Operation get becomes List<Operation> get) is often easier than diving into the weeds in order to add links between low level constructs.
  5. From the Swagger UI's standpoint, I'd venture to guess that the brain dead implementation, such as the one I was using despite it being a bug, would be easy to add. After which they could start thinking about a more merged UI model.
  6. It's 100% backwards compatible with the 2.0 spec.
  7. It solves my personal use case :smile:
cancan101 commented 9 years ago

Also related is #342

earth2marsh commented 9 years ago

:+1: on considering this for Swagger.Next. Supporting media-types seem like a worthy thing to support.

yonderblue commented 9 years ago

:+1: on support for different response types in some way, but would need to support based on header or param.

kevinconaway commented 9 years ago

:thumbsup: for this.

We are implementing an API similar to Github where we use the media type as a way to version our API. See the following Spring MVC code for an example

@Controller
@RequestMapping(value = "/api/service")
public class DemoController {

    @ApiOperation(value="Test v1")
    @RequestMapping(value = "/test", produces = "application/vnd.mycompany.v1+json", method = RequestMethod.GET)
    public String getV1() {
        return "v1";
    }

    @ApiOperation(value="Test v2")
    @RequestMapping(value = "/test", produces = {"application/vnd.mycompany+json", "application/vnd.mycompany.v2+json"}, method = RequestMethod.GET)
    public String getV2() {
        return "v2";
    }

}

Different versions could be accessed via the Accept header:

# version 1
curl -v "http://localhost/api/service/test" --header "Accept: application/vnd.mycompany.v1+json,application/vnd.mycompany+json"

# version 2
curl -v "http://localhost/api/service/test" --header "Accept: application/vnd.mycompany.v2+json,application/vnd.mycompany+json"

# latest version
curl -v "http://localhost/api/service/test" --header "Accept: application/vnd.mycompany+json"
notyalca commented 9 years ago

:+1:

zdila commented 9 years ago

+1

guangyang commented 9 years ago

I'd like to have this as well. This is very useful when I'm implementing some general pass through API where I want to have one method but if user input "plain/text", I return "plain/text" as a string in the response. And if they input "application/json", I return a json object.

ePaul commented 9 years ago

Just for reference, I just hit the same problem. ... We have (or will have, we are currently in the pre-design phase) an API which can produce for the same conceptual thing (resource) both a PDF and a JSON representation. It looks like I can't specify this with Swagger, as long as both have the same URL.

Our company's API guidelines also say that versioning (if incompatibilities can't be avoided) should be done by Accept header with new media types for new versions, which means we can't use one Swagger document to document both versions. (The same guidelines also tell me I have to use Swagger. :-/ )

About the syntax, I like the "schemas" proposal from mohsen1, though that needs to specify the content-types for each schema somehow. It seems like the second proposal from nickretallack has something here. (I'm not sure these aliases ("friendly names") are needed, maybe simply using the media types themselves as map keys would be enough.)