speccy icon indicating copy to clipboard operation
speccy copied to clipboard

Passing "resolveInternal" option causes "RangeError" when resolving a spec

Open mihirkothari25 opened this issue 5 years ago • 6 comments

Detailed description

This might be a bug with oas-resolver but the front-door we are using to resolve the OpenApi specs is speccy. Resolving refs works fine with the following options

const SPECCY_OPTIONS = {
  resolve: true,
  jsonSchema: true,
}

The above options cause the first external ref to be resolved, but subsequent external refs to that same file get converted to internal refs.

We have a use case where we need all the refs to be resolved and not have any internal refs, so I used the following options

const SPECCY_OPTIONS = {
  resolve: true,
  jsonSchema: true,
  resolveInternal: true,
}

This causes the following error -

RangeError: Maximum call stack size exceeded
at String.replace (<anonymous>)
at jpescape (/Users/mkothari/git/platform-specs/node_modules/reftools/lib/jptr.js:9:14)
at recurse (/Users/mkothari/git/platform-specs/node_modules/reftools/lib/recurse.js:34:60)
at recurse (/Users/mkothari/git/platform-specs/node_modules/reftools/lib/recurse.js:53:13)
at recurse (/Users/mkothari/git/platform-specs/node_modules/reftools/lib/recurse.js:53:13)
at recurse (/Users/mkothari/git/platform-specs/node_modules/reftools/lib/recurse.js:53:13)
at recurse (/Users/mkothari/git/platform-specs/node_modules/reftools/lib/recurse.js:53:13)
at recurse (/Users/mkothari/git/platform-specs/node_modules/reftools/lib/recurse.js:53:13)
at recurse (/Users/mkothari/git/platform-specs/node_modules/reftools/lib/recurse.js:53:13)
at recurse (/Users/mkothari/git/platform-specs/node_modules/reftools/lib/recurse.js:53:13)
  • Node Version: 12.18.0
  • Operating system and version (e.g. Ubuntu 16.04, Windows 7): Mac 10.15.5
  • Speccy Version: 0.11.0

mihirkothari25 avatar Jun 12 '20 16:06 mihirkothari25

An additional note - this the schema does have recursion and is structured as given in the example from the json-schema docs - https://json-schema.org/understanding-json-schema/structuring.html#recursion

mihirkothari25 avatar Jun 12 '20 20:06 mihirkothari25

A quick example test case - the yaml spec, say temp.yaml -

openapi: 3.0.2
info:
  title: api
  description: |
    description!
  contact:
    name: 'team'
    url: 'https://test.com/'
    email: '[email protected]'
  version: 0.0.1
servers:
  - url: https://test.com
paths: {}
components:
  responses:
    Request:
      description: request
      content:
        application/json:
          schema:
            $ref: temp.json#/definitions/person
tags: []

the schema in a file temp.json

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "person": {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "children": {
          "type": "array",
          "items": { "$ref": "#/definitions/person" },
          "default": []
        }
      }
    }
  },
  "type": "object",
  "properties": {
    "person": { "$ref": "#/definitions/person" }
  }
}

The command to run is - speccy resolve ./temp.yaml -j -v -i Error -

GET ./packages/common-spec/src/specifications/components/temp.yaml
GET /Users/mkothari/git/common-specs/packages/common-spec/src/specifications/components/temp.json #/definitions/person
RangeError: Maximum call stack size exceeded
    at String.replace (<anonymous>)
    at jpescape (/Users/mkothari/git/common-specs/node_modules/reftools/lib/jptr.js:9:14)
    at recurse (/Users/mkothari/git/common-specs/node_modules/reftools/lib/recurse.js:34:60)
    at recurse (/Users/mkothari/git/common-specs/node_modules/reftools/lib/recurse.js:53:13)
    at recurse (/Users/mkothari/git/common-specs/node_modules/reftools/lib/recurse.js:53:13)
    at recurse (/Users/mkothari/git/common-specs/node_modules/reftools/lib/recurse.js:53:13)
    at recurse (/Users/mkothari/git/common-specs/node_modules/reftools/lib/recurse.js:53:13)
    at recurse (/Users/mkothari/git/common-specs/node_modules/reftools/lib/recurse.js:53:13)
    at recurse (/Users/mkothari/git/common-specs/node_modules/reftools/lib/recurse.js:53:13)
    at recurse (/Users/mkothari/git/common-specs/node_modules/reftools/lib/recurse.js:53:13)
Maximum call stack size exceeded

mihirkothari25 avatar Jun 12 '20 21:06 mihirkothari25

What is your expected (non infinite) output if all internal references are to be replaced?

MikeRalphson avatar Jun 12 '20 21:06 MikeRalphson

That's a good question. IMO resolving it to the top level would suffice with a reference back to the object itself further down the tree.

This would be the desired output -

openapi: 3.0.2
info:
  title: api
  description: |
    description!
  contact:
    name: team
    url: https://test.com/
    email: [email protected]
  version: 0.0.1
servers:
  - url: https://test.com
paths: {}
components:
  responses:
    Request:
      description: request
      content:
        application/json:
          schema:
            type: object
            properties:
              name:
                type: string
              children:
                type: array
                items:
                  $ref: "#/components/responses/Request/content/application~1json/schema"
                default: []
tags: []

mihirkothari25 avatar Jun 12 '20 21:06 mihirkothari25

That is exactly the output if you don't specifiy resolveInternal: true.

MikeRalphson avatar Jun 13 '20 12:06 MikeRalphson

I think I simplified my test yaml too much without showcasing the actual use case I am looking to solve. If you refer to the same schema multiple times in the spec (which is what we are doing), as shown in the example below -

openapi: 3.0.2
info:
  title: api
  description: |
    description!
  contact:
    name: 'team'
    url: 'https://test.com/'
    email: '[email protected]'
  version: 0.0.1
servers:
  - url: https://test.com
paths: {}
components:
  responses:
    Request:
      description: request
      content:
        application/json:
          schema:
            $ref: temp.json#/definitions/person
    Request2:
      description: request
      content:
        application/json:
          schema:
            $ref: temp.json#/definitions/person
tags: []

if I don't specify resolveInternal: true then it results in this -

openapi: 3.0.2
info:
  title: api
  description: |
    description!
  contact:
    name: team
    url: https://test.com/
    email: [email protected]
  version: 0.0.1
servers:
  - url: https://test.com
paths: {}
components:
  responses:
    Request:
      description: request
      content:
        application/json:
          schema:
            type: object
            properties:
              name:
                type: string
              children:
                type: array
                items:
                  type: object
                  properties:
                    name:
                      type: string
                    children:
                      type: array
                      items:
                        $ref: "#/components/responses/Request/content/application~1json/schema/properti\
                          es/children/items"
                      default: []
                default: []
    Request2:
      description: request
      content:
        application/json:
          schema:
            $ref: "#/components/responses/Request/content/application~1json/schema"
tags: []

It resolves it the first time, and then any future references point to the first place it was resolved.

Our goal was to have it resolve in all spots, which is why we added resovleInternal: true, which then resulted in the RangeError.

mihirkothari25 avatar Jun 15 '20 15:06 mihirkothari25