open-api-spex / open_api_spex

Open API Specifications for Elixir Plug applications
Mozilla Public License 2.0
686 stars 179 forks source link

JSON Api helpers #12

Open austinh opened 6 years ago

austinh commented 6 years ago

Hey there! Love this project! Im currently trying to migrate my team’s elixir swagger docs from swagger format 2.0 to 3.0. (All of the rest of our teams use OpenApi 3.0 in their Rails apps)

My project also conforms to JSON API Schema. The old tool we were using (phoenix_swagger) had JSONAPI helpers. Ex: https://hexdocs.pm/phoenix_swagger/json-api-helpers.html#content They don’t support openapi 3.0 though, which is why we want to start using this library. JSONAPI helpers to make writing these Schemas smoother would be a great addition.

Is that something that could be added to this project?

mbuhot commented 6 years ago

Hey @austinh, yes I think the JsonApi helpers could be ported to open_api_spex, documenting JsonApi in swagger 'manually' is a real pain :D

Would you like to sketch out an API that would fit in with open_api_spex design style? I've tried to keep open_api_spex a little more explicit than phoenix_swagger with fewer macros.

austinh commented 6 years ago

Sure. I was thinking, for example, a Building model that had a one-to-one Address field as well as one-to-many Floors relationships that is paginated.

The main features I see of JSON API that are tedious to document are relationships and links (pagination).

The json response would be somewhat like this:

Building Api Sample:

{
   "meta":{
      "total":0,
      "page":0,
      "pages":0,
      "size":0
   },
   "data":[
      {
         "type":"building",
         "id":"string",
         "attributes":{
            "name":"string",
            "createdAt": "2018-06-02T04:06:06Z",
            "updatedAt": "2018-06-02T04:06:06Z",
         },
         "relationships":{
            "address":{
               "data":
                  {
                     "id":"string",
                     "type":"address"
                  }
            },
            "floors":{
               "data":[
                  {
                     "id":"string",
                     "type":"floor"
                  }
               ]
            }
         }
      }
   ],
  "links": {
    "self": "http://example.com/building?page[number]=3&page[size]=1",
    "first": "http://example.com/building?page[number]=1&page[size]=1",
    "prev": "http://example.com/building?page[number]=2&page[size]=1",
    "next": "http://example.com/building?page[number]=4&page[size]=1",
    "last": "http://example.com/building?page[number]=13&page[size]=1"
  }
}

The ability to define links and relationships easily would be great. Maybe to be explicit, when you define the relationship, you have to pass it another OpenApiSpex Schema (in this case you would pass it a singular Address schema and for the one-to-many you would pass it that schema, and a flag that lets the schema builder know that it should be an array.

mojidabckuu commented 4 years ago

@mbuhot @moxley I would like to raise this topic again. I know it was 2 years already after the last comment here, but I believe it is still the pain to write wrappers for data responses when we are all in "marcos" world now :)

I doubt about the implementation. The code for JsonAPI helpers from here is simply a wrapper and doesn't make anything except a new Schema. If I do the same without defining a module then using the generator(open api generator) I get default name because inlined schemas are generated with template names which are very long and not helpful for humans.

So each wrapper requires a new schema name like

def module UserReponse do
  require OpenApiSpex

    OpenApiSpex.schema(%{
    ...
    })
end

At the end I was thinking about something like this


@doc operation_id: :create_item,
       request_body: {"Item request", "application/json", SpecSchemas.request(SpecSchemas.Item), required: true},
       responses: %{
         200 => {"Response", "application/json", SpecSchemas.response(SpecSchemas.Item)}
       }

where SpecSchemas.request create a new schema with name convention from passed schema

      defmodule schema + "Request" do
        require OpenApiSpex

        OpenApiSpex.schema(%{
          type: :object,
          properties: %{
            "#{schema |> to_string() |> downcase() }": schema
          }
        })
      end

The same is applicable for the response.

But again, I don't know how to implement this in the right way. Am I able to use defmacro during the declaration in @doc? The marco returns AST and the resolve_schema_modules will fail.

krainboltgreene commented 2 months ago

I found https://www.columbia.edu/~alan/schemas/common/jsonapi.yaml as a helpful resource. It ~mostly~ decodes correctly.

mbuhot commented 2 months ago

Another source of inspiration for anyone looking to implement JSON:API with OpenAPISpex is the AshJsonApi.OpenApi module: https://github.com/ash-project/ash_json_api/blob/main/lib/ash_json_api/json_schema/open_api.ex

It maps Ash resources (attributes, relationships, calculated fields, etc.) onto OpenApi schemas.