Skip to content

Common response/payload object but different data defined per API? #773

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
atdiff opened this issue Aug 30, 2016 · 5 comments
Closed

Common response/payload object but different data defined per API? #773

atdiff opened this issue Aug 30, 2016 · 5 comments
Milestone

Comments

@atdiff
Copy link

atdiff commented Aug 30, 2016

What is the best way to represent a generic response/payload object that has basic fields such as message, total elements, data, and status? Where the variability between each API is the data element. For instance, one API could be for permissions, so the data element would hold an array of permissions. But for another API, it would hold a different array of object types. My main goal is to have reuse with a payload object and to define the next "layer" of actual data.

Here are some JSON samples of what's been attempted but isn't rendering the way we would expect it to in Swagger UI (i.e. a flat structure of 4 elements with data defined per the API).

Example 1 - composition:

{
    "swagger": "2.0",
    "host": "localhost:8888",
    "info": {
        "version": "0.0.1",
        "title": "API"
    },
    "paths": {
        "/permissions": {
            "get": {
                "description": "Returns all permissions",
                "operationId": "getPermissions",
                "produces": [
                    "application/json"
                ],
                "responses": {
                    "200": {
                        "description": "success",
                        "schema": {
                            "$ref": "#/definitions/permissionResponse"
                    }
                }
            }
        }
        }
    },
    "definitions": {
       "response": {
           "type": "object",
           "properties": {
              "message": {
                "type": "string",
                "description": "A string indicating the response from the server."
              },
              "totalElements": {
                "type": "integer",
                "format": "int64",
                "description": "The number of items retrieved."
              },
              "status": {
                "type": "string",
                "description": "A string indicating the response from the server."
              }
            }
        },
       "permissionResponse": {
         "allOf": [
                   {
          "$ref": "#/definitions/response"
        },{
           "type": "object",
           "properties": {
              "data": {
                "type": "array",
                "description": "The collection of items returned from the API request.",
                "items": {
                  "$ref": "#/definitions/permission"
                }
              }
            }
        }
            ]
        },
       "permission": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Unique identifier representing a specific permission."
                },
                "label": {
                    "type": "string",
                    "description": "The label of a permission."
                },
                "description": {
                    "type": "string",
                    "description": "A description of the permission."
                },
                "active": {
                    "type": "boolean",
                    "description": "A flag indicating whether a permission is active."
                }
            },
        "required": [
          "id",
          "label",
          "description",
          "active"
        ]
        }
    }
}

Example 2 - composition variation:

{
    "swagger": "2.0",
    "host": "localhost:8888",
    "info": {
        "version": "0.0.1",
        "title": "API"
    },
    "paths": {
        "/permissions": {
            "get": {
                "description": "Returns all permissions",
                "operationId": "getPermissions",
                "produces": [
                    "application/json"
                ],
                "responses": {
                    "200": {
                        "description": "success",
                        "schema": {
                            "$ref": "#/definitions/permissionResponse"
                    }
                }
            }
        }
        }
    },
    "definitions": {
       "response": {
           "type": "object",
           "properties": {
              "message": {
                "type": "string",
                "description": "A string indicating the response from the server."
              },
              "totalElements": {
                "type": "integer",
                "format": "int64",
                "description": "The number of items retrieved."
              },
              "status": {
                "type": "string",
                "description": "A string indicating the response from the server."
              }
            }
        },
       "permissionResponse": {
          "allOf":[
            {
              "$ref": "#/definitions/response" }        ],
           "type": "object",
           "properties": {
              "data": {
                "type": "array",
                "description": "The collection of items returned from the API request.",
                "items": {
                  "$ref": "#/definitions/permission"
                }
              }
            }
        },
       "permission": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Unique identifier representing a specific permission."
                },
                "label": {
                    "type": "string",
                    "description": "The label of a permission."
                },
                "description": {
                    "type": "string",
                    "description": "A description of the permission."
                },
                "active": {
                    "type": "boolean",
                    "description": "A flag indicating whether a permission is active."
                }
            },
        "required": [
          "id",
          "label",
          "description",
          "active"
        ]
        }
    }
}

Example 3 - additional properties:

{
    "swagger": "2.0",
    "host": "localhost:8888",
    "info": {
        "version": "0.0.1",
        "title": "API"
    },
    "paths": {
        "/permissions": {
            "get": {
                "description": "Returns all permissions",
                "operationId": "getPermissions",
                "produces": [
                    "application/json"
                ],
                "responses": {
                    "200": {
                        "description": "success",
                        "schema": {
                            "$ref": "#/definitions/permissionResponse"
                    }
                }
            }
        }
        }
    },
    "definitions": {
       "response": {
           "type": "object",
           "properties": {
              "message": {
                "type": "string",
                "description": "A string indicating the response from the server."
              },
              "totalElements": {
                "type": "integer",
                "format": "int64",
                "description": "The number of items retrieved."
              },
              "status": {
                "type": "string",
                "description": "A string indicating the response from the server."
              }
            }
        },
       "permissionResponse": {
           "type": "object",
           "properties": {
              "data": {
                "type": "array",
                "description": "The collection of items returned from the API request.",
                "items": {
                  "$ref": "#/definitions/permission"
                }
              }
            },
            "additionalProperties": {
              "$ref": "#/definitions/response"
            }
        },
       "permission": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Unique identifier representing a specific permission."
                },
                "label": {
                    "type": "string",
                    "description": "The label of a permission."
                },
                "description": {
                    "type": "string",
                    "description": "A description of the permission."
                },
                "active": {
                    "type": "boolean",
                    "description": "A flag indicating whether a permission is active."
                }
            },
        "required": [
          "id",
          "label",
          "description",
          "active"
        ]
        }
    }
}
@ePaul
Copy link
Contributor

ePaul commented Aug 31, 2016

If I understand right, you want support for generic data types.
Apart from simple maps (with string keys, being represented as JSON objects) or lists (being represented as JSON arrays), OpenAPI doesn't support that yet.

A related issue I found is #519.

@atdiff
Copy link
Author

atdiff commented Aug 31, 2016

@ePaul , yes sort of. I want to be able to define a data type that's generic - like a "response" that has basic fields but I want to be able to further define that content for each API (data containing permissions or roles or whatever).
The composition concept in Swagger (example 1 above) works great when I run it through swagger2markup and renders the content as I would hope/expect. SwaggerUI represents it as multiple objects and some nesting as a part of it too, so it's rather odd and confusing. This leaves me questioning if it's an issue with SwaggerUI, my Swagger structure, or something else. So trying to figure out the best way to represent the scenario in Swagger.

Markup rendered:
markup_rendered

Swagger UI rendered:
ui_rendered

Swagger UI rendered - expanded:
ui_rendered_expanded

@shyandsy
Copy link

will this to be supported in future?

@kscheirer
Copy link

I am evaluating this as a possible use case for overlays, OAI/Overlay-Specification#9.

I don't think overlays are needed here. I believe this can be represented in openapi3 as example (I did not test this)

{
  "openapi": "3.0.1",
  "info": {
    "title": "API",
    "version": "1.0"
  },
  "paths": {
    "/permissions": {
      "get": {
        "description": "Returns all permissions",
        "operationId": "getPermissions",
        "responses": {
          "200": {
            "description": "success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/permissionResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "permissionResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "A string indicating the response from the server."
          },
          "totalElements": {
            "type": "integer",
            "description": "The number of items retrieved.",
            "format": "int64"
          },
          "status": {
            "type": "string",
            "description": "A string indicating the response from the server."
          },
          "data": {
            "type": "array",
            "description": "The collection of items returned from the API request.",
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/permission",
                },
                {
                  "$ref": "#/components/schemas/role"
                }
              ]
              
            }
          }
        }
      },
      "role": {
        "type": "object",
        "properties": {
          "roleName": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        }
      },
      "permission": {
        "required": [
          "active",
          "description",
          "id",
          "label"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier representing a specific permission.",
            "format": "int64"
          },
          "label": {
            "type": "string",
            "description": "The label of a permission."
          },
          "description": {
            "type": "string",
            "description": "A description of the permission."
          },
          "active": {
            "type": "boolean",
            "description": "A flag indicating whether a permission is active."
          }
        }
      }
    }
  }
}

which looks like this at editor.swagger.io

Screen Shot 2022-08-24 at 8 57 31 PM

@handrews
Copy link
Member

This has been supported in OAS 3.1 by using dynamic references to implement generic types.

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

5 participants