Skip to content

Request for future spec version: More advanced inheritance among responses #537

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

Closed
xiehan opened this issue Jan 11, 2016 · 3 comments
Closed

Comments

@xiehan
Copy link

xiehan commented Jan 11, 2016

It was revealed to me in the course of swagger-api/swagger-js#680 that the current Swagger 2.0 spec doesn't really support an advanced inheritance model for Responses. Here's an example of what I was trying to do (using swagger-php because our API codebase is in PHP), creating an inheritance pattern to avoid too much copy-pasting of various definitions I'm using across-the-board:

/**
 * @SWG\Response(response=400,
 *   description="A bad request; generally, one or more parameters passed in were incorrect or missing",
 *   @SWG\Header(header="X-RateLimit-Limit",
 *     description="The number of allowed requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Remaining",
 *     description="The number of remaining requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Reset",
 *     description="The number of seconds left in the current period",
 *     type="integer"
 *   )
 * )
 * @SWG\Response(response="400WithDocument",
 *   ref="#/responses/400",
 *   @SWG\Schema(ref="#/definitions/ErrorDocument")
 * )

...but it was pointed out to me that this isn't valid as per the spec. The consequence is that this is what my PHP annotations now look like for my global Response definitions object:

/**
 * @SWG\Response(response=200,
 *   description="The request completed successfully",
 *   @SWG\Header(header="X-RateLimit-Limit",
 *     description="The number of allowed requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Remaining",
 *     description="The number of remaining requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Reset",
 *     description="The number of seconds left in the current period",
 *     type="integer"
 *   )
 * )
 * @SWG\Response(response=201,
 *   description="The request completed successfully and a new resource was created",
 *   @SWG\Header(header="X-RateLimit-Limit",
 *     description="The number of allowed requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Remaining",
 *     description="The number of remaining requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Reset",
 *     description="The number of seconds left in the current period",
 *     type="integer"
 *   )
 * )
 * @SWG\Response(response=202,
 *   description="The request has been accepted for processing, but the processing has not been completed.",
 *   @SWG\Header(header="X-RateLimit-Limit",
 *     description="The number of allowed requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Remaining",
 *     description="The number of remaining requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Reset",
 *     description="The number of seconds left in the current period",
 *     type="integer"
 *   )
 * )
 * @SWG\Response(response=204,
 *   description="The request was completed successfully, but there was no content to return",
 *   @SWG\Header(header="X-RateLimit-Limit",
 *     description="The number of allowed requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Remaining",
 *     description="The number of remaining requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Reset",
 *     description="The number of seconds left in the current period",
 *     type="integer"
 *   )
 * )
 * @SWG\Response(response=400,
 *   description="A bad request; generally, one or more parameters passed in were incorrect or missing",
 *   @SWG\Header(header="X-RateLimit-Limit",
 *     description="The number of allowed requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Remaining",
 *     description="The number of remaining requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Reset",
 *     description="The number of seconds left in the current period",
 *     type="integer"
 *   )
 * )
 * @SWG\Response(response=401, description="The client is not authorized to complete this request. Check to ensure a valid access token was passed in the headers.")
 * @SWG\Response(response=410, description="This API endpoint has been retired. Please upgrade to a newer version.")
 * @SWG\Response(response=429,
 *   description="The client has exceeded the number of daily calls as per their rate limit. For now, this only applies to prototype applications and untrusted clients. Trusted clients will never be rate-limited, nor will any production apps.",
 *   @SWG\Header(header="X-RateLimit-Limit",
 *     description="The number of allowed requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Remaining",
 *     description="The number of remaining requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Reset",
 *     description="The number of seconds left in the current period",
 *     type="integer"
 *   )
 * )
 * @SWG\Response(response=500,
 *   description="A server error",
 *   @SWG\Header(header="X-RateLimit-Limit",
 *     description="The number of allowed requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Remaining",
 *     description="The number of remaining requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Reset",
 *     description="The number of seconds left in the current period",
 *     type="integer"
 *   )
 * )
 * @SWG\Response(response=503,
 *   description="The system is undergoing maintenance and we are unable to fulfill this request. Look for a `Retry-After` header to see the predicted time the system will be back up.",
 *   @SWG\Header(header="Retry-After",
 *     description="The predicted time the system will be back up",
 *     type="string",
 *     format="date-time"
 *   )
 * )
 *
 * @SWG\Response(response="400WithDocument",
 *   description="A bad request; generally, one or more parameters passed in were incorrect or missing",
 *   @SWG\Header(header="X-RateLimit-Limit",
 *     description="The number of allowed requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Remaining",
 *     description="The number of remaining requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Reset",
 *     description="The number of seconds left in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Schema(ref="#/definitions/ErrorDocument")
 * )
 * @SWG\Response(response="401WithDocument",
 *   description="The client is not authorized to complete this request. Check to ensure a valid access token was passed in the headers.",
 *   @SWG\Schema(ref="#/definitions/ErrorDocument")
 * )
 * @SWG\Response(response="429WithDocument",
 *   description="The client has exceeded the number of daily calls as per their rate limit. For now, this only applies to prototype applications and untrusted clients. Trusted clients will never be rate-limited, nor will any production apps.",
 *   @SWG\Header(header="X-RateLimit-Limit",
 *     description="The number of allowed requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Remaining",
 *     description="The number of remaining requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Reset",
 *     description="The number of seconds left in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Schema(ref="#/definitions/ErrorDocument")
 * )
 * @SWG\Response(response="500WithDocument",
 *   description="A server error",
 *   @SWG\Header(header="X-RateLimit-Limit",
 *     description="The number of allowed requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Remaining",
 *     description="The number of remaining requests in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Header(header="X-RateLimit-Reset",
 *     description="The number of seconds left in the current period",
 *     type="integer"
 *   ),
 *   @SWG\Schema(ref="#/definitions/ErrorDocument")
 * )
 * @SWG\Response(response="503WithDocument",
 *   description="The system is undergoing maintenance and we are unable to fulfill this request. Look for a `Retry-After` header to see the predicted time the system will be back up.",
 *   @SWG\Header(header="Retry-After",
 *     description="The predicted time the system will be back up",
 *     type="string",
 *     format="date-time"
 *   ),
 *   @SWG\Schema(ref="#/definitions/ErrorDocument")
 * )
 */

...in other words, there's a massive amount of copy-pasting there that's going to be extremely difficult to maintain over time, especially as we're starting to use headers more intelligently in our API. In general, our API is quite large and growing all the time, and we're trying to be really conscious of using consistent, clear language across the board, so anytime I have to define something more than once by copy-pasting, that makes that goal that much more difficult.

I realize that some of this is inherent in swagger-php's use of annotations, as opposed to a code-based solution where you could just assign your response properties to a variable and then reuse them elsewhere. But IIRC swagger-php isn't the only Swagger project using an annotation-based approach, so we're not the only ones who are going to encounter this difficulty. (FWIW I did open a ticket with swagger-php for some kind of code-based solution -- zircote/swagger-php#274 -- to at least tide us over in the short term.)

It seems to me like responses could definitely benefit from the kind of composition and inheritance you can do with regular model definitions. I'm not particularly invested in one solution over another (my first example is just what seemed intuitive to me based on what I'd been doing for model definitions, and not because I believe it's "right") as long as it gets us to a point where we can reuse/share response properties (and ideally headers, too) more easily.

@webron
Copy link
Member

webron commented Jan 11, 2016

Thanks for opening the ticket. #369 is related but more isolated. #521 can also potentially help avoiding repetitions in some cases. This issue is broader and can tackle general response definitions.

@fehguy
Copy link
Contributor

fehguy commented Mar 1, 2016

Parent issues #560, #579

@fehguy
Copy link
Contributor

fehguy commented Mar 10, 2016

I'm going to close this out in favor of #563. We don't want you to have to copy/paste responses all over the place, and #563 has the required information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants