python-openapi / openapi-core

Openapi-core is a Python library that adds client-side and server-side support for the OpenAPI v3.0 and OpenAPI v3.1 specification.
BSD 3-Clause "New" or "Revised" License
290 stars 131 forks source link

[Bug]: External reference not working properly #565

Open jonathan-daniel opened 1 year ago

jonathan-daniel commented 1 year ago

Actual Behavior

I have a schema called mobile_api.json, containing an external reference to another schema called client_api.json, the external reference works, but the client_api.json schema also contain an internal reference, which the resolver doesn't find, are you aware of a solution for this?

stack trace:

\"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/openapi_core/shortcuts.py\", line 173, in unmarshal_request\n return unmarshal_apicall_request(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/openapi_core/shortcuts.py\", line 116, in unmarshal_apicall_request\n result = v.unmarshal(request)\n ^^^^^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/openapi_core/unmarshalling/request/unmarshallers.py\", line 243, in unmarshal\n return self._unmarshal(request, operation, path)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/openapi_core/unmarshalling/request/unmarshallers.py\", line 154, in _unmarshal\n body = self._get_body(request.body, request.mimetype, operation)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/openapi_core/validation/decorators.py\", line 31, in wrapper\n return f(*args, **kwds)\n ^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/openapi_core/validation/request/validators.py\", line 253, in _get_body\n return self._get_content_value(raw_body, mimetype, content)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/openapi_core/unmarshalling/unmarshallers.py\", line 113, in _get_content_value\n return self._unmarshal_schema(schema, casted)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/openapi_core/unmarshalling/unmarshallers.py\", line 90, in _unmarshal_schema\n return unmarshaller.unmarshal(value)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/openapi_core/unmarshalling/schemas/unmarshallers.py\", line 288, in unmarshal\n self.schema_validator.validate(value)\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/openapi_core/validation/schemas/validators.py\", line 38, in validate\n errors = tuple(errors_iter)\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/jsonschema/validators.py\", line 288, in iter_errors\n for error in errors:\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/jsonschema/_validators.py\", line 332, in properties\n yield from validator.descend(\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/jsonschema/validators.py\", line 305, in descend\n for error in self.evolve(schema=schema).iter_errors(instance):\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/jsonschema/validators.py\", line 288, in iter_errors\n for error in errors:\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/jsonschema/_validators.py\", line 294, in ref\n scope, resolved = validator.resolver.resolve(ref)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/jsonschema/validators.py\", line 898, in resolve\n return url, self._remote_cache(url)\n ^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/jsonschema/validators.py\", line 916, in resolve_from_url\n return self.resolve_fragment(document, fragment)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Users/jonathan/snowfall/junction/dist/export/python/virtualenvs/python-default/3.11.1/lib/python3.11/site-packages/jsonschema/validators.py\", line 968, in resolve_fragment\n raise exceptions.RefResolutionError(\njsonschema.exceptions.RefResolutionError: Unresolvable JSON pointer: 'components/schemas/TripCandidateSearch'"}

Expected Behavior

Find the internal reference in the external referenced schema.

Steps to Reproduce

example mobile_api.json reference:

...
"paths": {
    "/cabin-classes": {
      "$ref": "./../client_api/openapi.json#/paths/~1cabin-classes"
    },
}

client_api.json:

...
"paths": {
    "/cabin-classes": {
      "parameters": [],
      "get": {
        "summary": "List Cabin Classes",
        "tags": [
          "cabins"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/CabinClass" <---- internal reference will break
                          }
                        }
                      },
                      "required": [
                        "data"
                      ]
                    },
                    {
                      "$ref": "#/components/schemas/NextLink"
                    }
                  ]
                },
                "examples": {
                  "example": {
                    "value": {
                      "data": [
                        {
                          "id": "unspecified",
                          "type": "cabin-classes",
                          "attributes": {
                            "name": "unspecified"
                          },
                          "links": {
                            "self": "https://example.com/cabin-classes/unspecified"
                          }
                        },
                        {
                          "id": "economy",
                          "type": "cabin-classes",
                          "attributes": {
                            "name": "economy"
                          },
                          "links": {
                            "self": "https://example.com/cabin-classes/economy"
                          }
                        },
                        {
                          "id": "business",
                          "type": "cabin-classes",
                          "attributes": {
                            "name": "business"
                          },
                          "links": {
                            "self": "https://example.com/cabin-classes/business"
                          }
                        }
                      ],
                      "links": {
                        "next": "https://example.com/cabin-classes"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "get-cabin-classes",
        "description": "Used to retrieve available Cabin Classes.",
        "parameters": [
          {
            "$ref": "#/components/parameters/cabinClassSort"
          },
          {
            "$ref": "#/components/parameters/page-cursor"
          },
          {
            "$ref": "#/components/parameters/page-limit"
          }
        ]
      }
    },
}

OpenAPI Core Version

0.17.1

OpenAPI Core Integration

starlette

Affected Area(s)

schema, unmarshalling

References

No response

Anything else we need to know?

No response

Would you like to implement a fix?

Yes

jonathan-daniel commented 1 year ago

It works when I use the path to client_api like an external reference (for an internal reference) as if it was from mobile_api

How can I make jsonschema find internal references from both schemas?

redparham commented 10 months ago

I can confirm that I'm seeing the same issue