whitlockjc / json-refs

Various utilities for JSON Pointers (http://tools.ietf.org/html/rfc6901) and JSON References (http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03).

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Resolving nested references

CsatariGergely opened this issue · comments

Hi,
I would like to use nested references, when a reference contains an other reference. The aim to do this is to collect all external OpenAPI definitions into the main OpenAPI file, but not to inline the definitions.
I have an OpenAPI file a.yaml, like:

{
  "swagger": "2.0",
  "info": {
    "version": "2",
    "title": "a"
  },
  "basePath": "/a/v1",
  "schemes": [
    "https"
  ],
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "paths": {
    "/a": {
      "post": {
        "description": "A.",
        "parameters": [
          {
            "name": "A",
            "description": "A",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "definitions/definitions.yaml#/definitions/A"
            }
          }
        ]
      }
    }
  },
  "definitions": {
    "A": {
      "type": "object",
      "required": [
        "Aa"
      ],
      "properties": null,
      "Aa": {
        "type": "string"
      },
      "Ab": {
        "description": "Ab\n",
        "type": "string"
      },
      "Ac": {
        "description": "Ac\n",
        "type": "string"
      }
    }
  }
}

and the definitions are in an external file ./definitions/definitions.yaml:

definitions:
    A:
        type: object
        required:
        - Aa
        properties:
        Aa:
            description: >
                Aa
            $ref: "#/definitions/Aa"
        Ab:
            description: >
                Ab
            type: string
        Ac:
            description: >
                Ac
            type: string

    Aa:
        type: string 

After json-refs-ing I get:

{
  "swagger": "2.0",
  "info": {
    "version": "2",
    "title": "a"
  },
  "basePath": "/a/v1",
  "schemes": [
    "https"
  ],
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "paths": {
    "/a": {
      "post": {
        "description": "A.",
        "parameters": [
          {
            "name": "A",
            "description": "A",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "definitions/definitions.yaml#/definitions/A"
            }
          }
        ]
      }
    }
  },
  "definitions": {
    "A": {
      "type": "object",
      "required": [
        "Aa"
      ],
      "properties": null,
      "Aa": {
        "type": "string"
      },
      "Ab": {
        "description": "Ab\n",
        "type": "string"
      },
      "Ac": {
        "description": "Ac\n",
        "type": "string"
      }
    }
  }
}

While my target would be:

{
  "swagger": "2.0",
  "info": {
    "version": "2",
    "title": "a"
  },
  "basePath": "/a/v1",
  "schemes": [
    "https"
  ],
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "paths": {
    "/a": {
      "post": {
        "description": "A.",
        "parameters": [
          {
            "name": "A",
            "description": "A",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/A"
            }
          }
        ]
      }
    }
  },
  "definitions": {
    "A": {
      "type": "object",
      "required": [
        "Aa"
      ],
      "properties": null,
      "Aa": {
        "type": "string"
      },
      "Ab": {
        "description": "Ab\n",
        "type": "string"
      },
      "Ac": {
        "description": "Ac\n",
        "type": "string"
      }
    }
  }
}

I'm open to any solution ideas.
Any ideas if this is a new feature or a bug in json-refs?

Thanks

Things are working as designed and is documented here: https://github.com/whitlockjc/json-refs/tree/master/docs#resolution When it comes to resolution, you can do remote-only resolution but anytime you reference a remote document, or a portion of the document outside of options.subDocPath, these references will always be resolved completely. The reason for this is that if you do not do this, you can end up with a partially resolved document.

If after using json-refs you have unresolved references, like you do at ['paths', '/a', 'post', 'parameters', '0', 'schema'], that means there was an error resolving your references. To identify what the resolution issue was, when you use JsonRefs.resolveRefs, you will get resolution metadata and any errors will be listed there. I'll try to reproduce locally and share what I find.

When I tried to reproduce your issue, I get a complete resolution as expected:

{
  "swagger": "2.0",
  "info": {
    "version": "2",
    "title": "a"
  },
  "basePath": "/a/v1",
  "schemes": [
    "https"
  ],
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "paths": {
    "/a": {
      "post": {
        "description": "A.",
        "parameters": [
          {
            "name": "A",
            "description": "A",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "required": [
                "Aa"
              ],
              "properties": null,
              "Aa": {
                "type": "string"
              },
              "Ab": {
                "description": "Ab\n",
                "type": "string"
              },
              "Ac": {
                "description": "Ac\n",
                "type": "string"
              }
            }
          }
        ]
      }
    }
  },
  "definitions": {
    "A": {
      "type": "object",
      "required": [
        "Aa"
      ],
      "properties": null,
      "Aa": {
        "type": "string"
      },
      "Ab": {
        "description": "Ab\n",
        "type": "string"
      },
      "Ac": {
        "description": "Ac\n",
        "type": "string"
      }
    }
  }
}

Thanks for the answer. It is interesting that you had a different result than me. I use json-refs via the CLI. I added my example files and the result here: json-refs-example.zip
Is there a way to know what options.subDocPath is used and what is the metadata of my resolution?
What I would like to achieve is to get the definitions of my OpenAPI into the same file, but still referring to them. This is becouse these definitions are used in a lots of places and resolving them inplace creates very big files.

What version of json-refs are you using?

I use version 3.0.0.

Yes, you can view the resolution metadata. Basically, JsonRefs#resolveRefs returns a Promise that itself returns a ResolvedRefsResults object which contains a refs property containing each unique reference identified and the result of the resolution.

As for the objects being referenced a lot and creating large files, I get it. The good news is that in memory, json-refs is extremely efficient and will only resolve references once, no matter the lineage to the reference. And since JavaScript treats array/object references as cheap,a s in not copied, json-refs handles complex documents with tons of references. Where things are not ideal is when you stringify the resolved document and this is due to the nature of JSON representation. We could potentially update json-refs to resolve only remote references without having to resolve all nested references completely but this isn't a simple task. It would require us turning local references in remote documents into local references in the resulting document by merging these referenced objects into the resolved document. I thought about this once and my brain started hurting. I'm not saying it's impossible, it's just not something I wanted to tackle when rewriting the resolver for 3.0.0. Maybe it's time to revisit it?

Lastly, I'll look into your example and report what I find.

I think this is a bug. It looks like the local reference is being resolved prior to the remote reference and that shouldn't happen. I'll see if I can reproduce this in a simpler test and go from there.

This has been fixed and is resolved in json-refs@3.0.2.

Thanks for the fix.

A possible workaround would be when json-refs would not resolve the local references only the remote ones. In this way several occurences could point to one local reference which would point to the remote one?

The issue is more about how we're updating values as part of resolution. If I update #/A to point to #/B, so long as I only modify the content of #/B and do not replace #/B in its entirety, things will work fine. So if #/B contains references within it, things will work fine. It's only when #/B itself is a reference, so resolving it replaces the complete content of #/B, that this is an issue. In all honesty, we probably could had reproduced this with local references only.