go-openapi / spec

openapi specification object model

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

spec.ExpandSpec doesn't seem to terminate in some swagger spec with recursive references

elakito opened this issue · comments

Hi,
I noticed this problem when generating a go client from one of the odata-openapi samples. The generation gets stuck and looking into this problem revealed that the call to spec.EpandSpec is not coming back. While looking at the recursive iterations that go into expandSchema in expander.go, I am wondering why we are not doing something like isSeen check in addition to isCircular check.

I put a test case that shows this issue at my forked repo.
https://github.com/elakito/spec/blob/issue-expand/expander_test.go#L454
and this is the swagger file used.
https://github.com/elakito/spec/blob/issue-expand/fixtures/expansion/circularSpec2.json

If someone can comment on this issue, that would be appreciated.

regards, aki

The "isSeen" check is supposed to be the "parentRefs".
I found a first bug with unnormalized parent refs in isCircular, but this is not enough to resolve your test case.

Looks like when the same $ref participates to several cycles, the simple path walking algorithm fails.
If I cache somewhere a memory of previous circular ref found, the hang is resolved.
Let's see how it goes with more extended tests...

My solution works, but not as expected: depending on which cycle was first detected, the expanded result varies from run to run. Trying another way...

Here is a more tractable test case demonstrating this issue.

swagger: "2.0"
info: 
  version: "0.0.1"
basePath: "/"
paths: 
  /cycles: 
    get: 
      responses: 
        200: 
          description: "ok"
          schema: 
            $ref: "#/definitions/node0"
definitions: 
  node0:
    type: object
    properties:
      p00:
        $ref: '#/definitions/node1'
      p01:
        $ref: '#/definitions/node3'
  node1:
    type: object
    properties:
      p1:
        $ref: '#/definitions/node2'
  node2:
    type: object
    properties:
      p2:
        $ref: '#/definitions/node0'
  node3:
    type: object
    properties:
      p3:
        $ref: '#/definitions/node1'

@elakito I did some research on this today and produced a PR which is more like a work around.

Your comments are welcome.

Fixed with PR#81.
However there are some limitations with this fix, that I am going to detail in more targeted issues.

@elakito feel free to reopen this if you think we missed an important point.

@fredbi looks good. Thanks

Hello @elakito

While fixing this with @pytlesk4, it occurred that we covered this use case, but some more devious
issues still remain. I did not find time to open a specific issue on those.

So for the record:

  • expansion result with several cycles sharing a spec node remains non-deterministic, and the shortest cycle is not necessarily the one retained
  • when cycles are formed in a remote, the resulting $ref is left as remote, so the expanded result
    is not entirely contained in a single root

Those are tricky cases...