Skip to content

Ability to define generic wrapper around the output #519

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
jesmith17 opened this issue Dec 2, 2015 · 16 comments
Closed

Ability to define generic wrapper around the output #519

jesmith17 opened this issue Dec 2, 2015 · 16 comments
Milestone

Comments

@jesmith17
Copy link

I am working on using Swagger to define my API's that are complying with the JSON-API spec.

It would be helpful if there was a way to wrapper the defined objects (and the parameters) with the JSON-API specific formatting. Ideally allowing the developers of the documentation to focus just on the contents of their custom content, and not have to duplicate the wrapper code.

Looking at JSON-API you are talking about having the top level root element, normally "data", and then the generic "type, id, attributes" elements that sit above each custom object.

Is that something that could be considered to be added to the spec?

@webron
Copy link
Member

webron commented Dec 2, 2015

Sure, thanks for bringing that up. Somewhat reminds me of a SOAP envelope (in concept, not meaning).

To help us better assess this, can you provide more details on what the structure would be like with JSON-API? It'd help rather than to dive into the whole spec and digging up the relevant details.

Also, if you're familiar with other API specifications that require something similar, it would help to have references to those as well.

@jharmn
Copy link
Contributor

jharmn commented Jan 8, 2016

The examples on http://jsonapi.org are pretty self-descriptive of this issue (and apply to HAL and potentially other message formats):
http://jsonapi.org/

@ryan0x44
Copy link

ryan0x44 commented Feb 3, 2016

I'm very interested in this area as well. I did a talk on JSON API last week, if you skip to slide 13 you might be able to get a quicker overview of the key features of JSON API than reading the whole spec (I hope!). However, I'm not sure if this idea can be implemented well without studying the spec in detail.

The other thing that would need to be considered is having a way to specify in the YAML file whether or not JSON API features such as pagination are supported by the server.

It would be really cool if we could specify a 'jsonapi' object directly under the location which could be automatically expanded by some sort of JSON API plugin to Swagger/OpenAPI. e.g:

paths:
  /blog/posts:
    jsonapi:
      summary: "List of blog posts"
      type: 'collection'
      features:
        pagination: true
        data:
          $ref: '#/definitions/BlogPost'
        errors:
          - $ref: '#/definitions/BlogPostError'
        included:
          - $ref: '#/definitions/BlogAuthor'
          - $ref: '#/definitions/BlogComment'

This type of thing would abstract the YAML file from details such as headers and query parameters, which I think is great as it would allow API designers to focus on business logic and not the API implementation details - which is a great strength of JSON API.

@jharmn
Copy link
Contributor

jharmn commented Feb 5, 2016

Just brainstorming here:
JSON Schema for json-api is here: http://jsonapi.org/schema
We could define an allOf to inherit from that model:

allOf:
- $ref: 'Pet.yaml'
- $ref: 'http://jsonapi.org/schema'

However, if Pet.yaml redefined anything in the json-api schema, that would violate our REUSE guidelines, right? I'd imagine this is a big problem to resolve which reference's fields win when merging (as it's not parent, but an array of definitions.

Whether you reference definitions locally or remote, you can never override or change their definitions from the referring location. The definitions can only be used as-is.

I guess I'm just making sure we disquality allOf as a solution, if I'm thinking about this right.

@webron
Copy link
Member

webron commented Feb 5, 2016

Why would it violate the REUSE guidelines? JSON Schema allows you to define conflicting schema under allOf. It's useless, but it doesn't violate it. However, if we're looking to make things easier on users, we may want to allow for an easier construct (allOf is fairly horrible, imho). Since wrappers have been requested for other uses as well (pagination, for example), it doesn't have to be specific.

@jharmn
Copy link
Contributor

jharmn commented Feb 5, 2016

@webron the point is that if Pet.json and http://jsonapi.org/schema both define items, how are those fields merged? Clearly the intent is that Pet.json/items overrides http://jsonapi.org/schema/items, but there's really no way to determine that programatically.

Suffice it to say I still don't really understand what that clause in REUSE means.

...and yes, allOf is terrible. We should all be commenting on the v5 proposals en masse about oneOf, allOf, anyOf: https://github.com/json-schema/json-schema/wiki/v5-Proposals

@webron
Copy link
Member

webron commented Feb 5, 2016

allOf is does not merge objects. allOf means that a given value is validated against all-of the listed schema, one by one. So yeah, they can be conflicting and not make sense. Now, if items is defined in both, in such a way that you can write a value that validates against both, it's fine, but JSON Schema does not treat it as a merged schema.

@fehguy
Copy link
Contributor

fehguy commented Mar 1, 2016

Parent issues #579, #586

@adhamhf
Copy link

adhamhf commented May 17, 2017

I'm having a discussion with Ron related to this in swagger-api/swagger-ui#3073.

I'm not sure if this is wrapper is needed. Swagger is agnostic about the data exchange format, so you shouldn't have to specify anything other than consumes and produces. Other tools can use consumes and produces to determine what format to use to exchange data.

As an example, if we have to encode information about the data exchange format into the spec, then we are limited to only using that format for our paths. Instead, if we just use the consumes and produces arrays we can have multiple formats from the same path. This is useful when your app can use the same endpoint for multiple clients and return different formats based on the client. ( Spring can do this. )

We are using JSON-API with Spring and Ember. We generate controllers and models from the swagger specification. The Spring and Ember frameworks are then in charge of converting the models into JSON-API for sending over the wire.

By keeping the data exchange format out of the swagger spec you keep it flexible and agnostic.

@carldunham
Copy link

Something is needed. It's horrible trying to specify anything using inheritance because of the brokenness of allOf. I hope that can be fixed in JSON-Schema, but in the meantime, a useful method here would be appreciated.

@SarasovMatvey
Copy link

It's already 2022. How's it going?

1 similar comment
@vipcxj
Copy link

vipcxj commented Aug 28, 2022

It's already 2022. How's it going?

@TekExplorer
Copy link

it doesnt seem very difficult to do...

@Pangamma
Copy link

Back again. Do we have a solution for this?

@jdesrosiers
Copy link
Contributor

There is a way to do this with OpenAPI 3.1 because it supports dynamic references from JSON Schema 2020-12.

Imagine we have a pretty standard CRUD API. Each resource also has a list operation, but we want to include paging information with the list responses. That means we need a wrapper over the array and we want that wrapper to be the same for every list in our system.

Without dynamic references, we'd have to do something like this for each resource, duplicating the list wrapper every time. If at some point we want to change this wrapper, we would have to change it for every resource. Some of this duplication can be addressed with normal schema composition (allOf), but not all.

{
  "$id": "https://json-schema.hyperjump.io/schema/foo-list"

  "type": "object",
  "properties": {
    "list": {
      "type": "array",
      "items": { "$ref": "/schema/foo" }
    },
    "nextPage": { "type": "integer" },
    "previousPage": { "type": "integer" },
    "perPage": { "type": "integer" },
    "page": { "type": "integer" }
  }
}

However, with dynamic references, we can make this wrapper generic.

{
  "$id": "https://json-schema.hyperjump.io/schema/list"

  "type": "object",
  "properties": {
    "list": {
      "type": "array",
      "items": { "$dynamicRef": "#list" }
    },
    "nextPage": { "type": "integer" },
    "previousPage": { "type": "integer" },
    "perPage": { "type": "integer" },
    "page": { "type": "integer" }
  },

  "$defs": {
    "default-list": { "$dynamicAnchor": "list" }
  }
}

In this schema, "$dynamicRef": "#list, becomes a sort of placeholder that can be filled in by another schema that references this one.

{
  "$id": "https://json-schema.hyperjump.io/schema/foo-list"

  "$ref": "/schema/list",

  "$defs": {
    "foo": {
      "$dynamicAnchor": "list",
      "$ref": "/schema/foo"
    }
  }
}

Here, the "list" dynamic anchor is set to point to the schema for the "foo" resource. There is zero duplication of the wrapper. Anything about the wrapper can change and it only needs to change in one place.

There are a few downsides to this approach. Mainly, it's a lot of boilerplate and can be difficult to understand. Also, dynamic references aren't yet supported in a lot places including ajv and vscode.

@handrews
Copy link
Member

The previous comment shows how to do this in 3.1. Dynamic references are also now supported in more tools. See also Using Dynamic References to Support Generic Types.

@handrews handrews added this to the v3.1.1 milestone May 22, 2024
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

14 participants