Skip to content

Conversation

@mikeharder
Copy link
Member

@mikeharder mikeharder commented May 23, 2024

@johanste, @markcowl, @JeffreyRichter, @allenjzhang, @mikekistler: Please object if anything about this change looks wrong to you.

In summary, the change always calls dereference() on a property with a $ref, before comparing equality.

This looks pretty safe to me and @konrad-jamrozik, and passes the new unit test we wrote (plus all existing tests).

@mikeharder mikeharder self-assigned this May 23, 2024
@konrad-jamrozik
Copy link

konrad-jamrozik commented May 23, 2024

I see the test fails with incompatible properties : bar.

I took the spec:

{
  "swagger": "2.0",
  "info": {
    "title": "compatible-properties-ref",
    "version": "1.0"
  },
  "paths": {
  },
  "definitions": {
    "FooBarString": {
      "type":"object",
      "properties": {
        "bar": {
          "type":"string"
        }
      },
      "allOf": [
        {
          "$ref": "#/definitions/FooBarStringRef"
        }
      ]
    },
    "FooBarStringRef": {
      "type":"object",
      "properties": {
        "bar": {
          "$ref":"#/definitions/MyString"
        }
      }
    },
    "MyString": {
      "type": "string"
    }
  }
}

and pasted it here:

I got no errors in both cases.

image

Looking at that screenshot, both definitions have the same bar. There is no incompatibility anywhere. So the spec appears to be OK. The tool interpretation must be wrong.

Interestingly, when I make MyString have different type and extra key example:

image

Then FooBarString picks up the example but ignores the different type:

image

@konrad-jamrozik
Copy link

konrad-jamrozik commented May 23, 2024

OK I think I know what is the root-cause. The problem lies in openapi-diff logic.

We throw error from here

Which will happen if this line returns true:

if (!this.isEqual(allOfSchema.properties[key], schemaList[key])) {

On the failing test it evaluates to:

this.isEqual({"$ref":"#/definitions/MyString"}, {"type":"string"})

Now inside the this.isEqual logic the problem lies here:

if ((!parentProperty.type || parentProperty.type === "object") && (!unwrappedProperty.type || unwrappedProperty.type === "object")) {

On the failing test it evaluates to:

(!undefined || undefined === "object") && (!string || string === "object")

which is (true && false) which is false.

As a result, the isEqual will do no more dereferencing, so the parentProperty, which is {"$ref":"#/definitions/MyString"}, won't be dereferenced. Hence isEqual will return with the last return:

return parentProperty.type === unwrappedProperty.type && parentProperty.format === unwrappedProperty.format

which will evaluate like that:

parentProperty.type (undefined) === unwrappedProperty.type (string) && parentProperty.format (undefined) === unwrappedProperty.format (undefined)
which is
undefined === string && undefined === undefined which is false, hence we throw.

Basically, the logic of isEqual is assuming that it has either dereference both parentProperty and unwrappedProperty, or none, but not possibly only one.

Konrad Jamrozik and others added 2 commits May 23, 2024 01:10
Co-authored-by: Mike Harder <mharder@microsoft.com>
Fix the `incompatible properties` problem
@mikeharder mikeharder marked this pull request as ready for review May 23, 2024 08:11
@konrad-jamrozik
Copy link

@mikeharder I think you may want to change the PR title before merging to mention it fixes the bug.

@mikeharder mikeharder changed the title Add test for compatible properties with a $ref Ensure properties are dereferenced before comparing equality May 23, 2024
Copy link
Member

@mikekistler mikekistler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Error "incompatible properties" for properties compatible via $ref

4 participants