Repository: marshmallow-code/apispec
Pull request #359 (2019-01-24 09:21:15)

Fix: Use existing reference if name resolver returns None
This reverses the logic in `resolve_nested_schema` to give priority to existing reference.


Patch:
diff --git a/apispec/ext/marshmallow/openapi.py b/apispec/ext/marshmallow/openapi.py
index c61310a..d334a04 100644
--- a/apispec/ext/marshmallow/openapi.py
+++ b/apispec/ext/marshmallow/openapi.py
@@ -403,25 +403,22 @@ class OpenAPIConverter(object):
 
         :param schema: schema to add to the spec
         """
-
-        schema_cls = self.resolve_schema_class(schema)
-        name = self.schema_name_resolver(schema_cls)
-
-        if not name:
-            try:
-                return self.schema2jsonschema(schema)
-            except RuntimeError:
-                raise APISpecError(
-                    'Name resolver returned None for schema {schema} which is '
-                    'part of a chain of circular referencing schemas. Please'
-                    ' ensure that the schema_name_resolver passed to'
-                    ' MarshmallowPlugin returns a string for all circular'
-                    ' referencing schemas.'.format(schema=schema),
-                )
-
         schema_instance = resolve_schema_instance(schema)
         schema_key = make_schema_key(schema_instance)
         if schema_key not in self.refs:
+            schema_cls = self.resolve_schema_class(schema)
+            name = self.schema_name_resolver(schema_cls)
+            if not name:
+                try:
+                    return self.schema2jsonschema(schema)
+                except RuntimeError:
+                    raise APISpecError(
+                        'Name resolver returned None for schema {schema} which is '
+                        'part of a chain of circular referencing schemas. Please'
+                        ' ensure that the schema_name_resolver passed to'
+                        ' MarshmallowPlugin returns a string for all circular'
+                        ' referencing schemas.'.format(schema=schema),
+                    )
             name = get_unique_schema_name(self.spec.components, name)
             self.spec.components.schema(
                 name,


Test patch:
diff --git a/tests/test_ext_marshmallow.py b/tests/test_ext_marshmallow.py
index 72271ad..4437a29 100644
--- a/tests/test_ext_marshmallow.py
+++ b/tests/test_ext_marshmallow.py
@@ -425,6 +425,66 @@ class TestOperationHelper:
             spec_fixture.spec,
         ) + 'Pet'
 
+    def test_schema_uses_ref_if_available_name_resolver_returns_none_v2(self):
+        def resolver(schema):
+            return None
+        spec = APISpec(
+            title='Test auto-reference',
+            version='0.1',
+            openapi_version='2.0',
+            plugins=(
+                MarshmallowPlugin(schema_name_resolver=resolver,),
+            ),
+        )
+        spec.components.schema('Pet', schema=PetSchema)
+        spec.path(
+            path='/pet',
+            operations={
+                'get': {
+                    'responses': {
+                        200: {
+                            'schema': PetSchema,
+                        },
+                    },
+                },
+            },
+        )
+        get = get_paths(spec)['/pet']['get']
+        assert get['responses'][200]['schema']['$ref'] == ref_path(spec) + 'Pet'
+
+    def test_schema_uses_ref_if_available_name_resolver_returns_none_v3(self):
+        def resolver(schema):
+            return None
+        spec = APISpec(
+            title='Test auto-reference',
+            version='0.1',
+            openapi_version='3.0.0',
+            plugins=(
+                MarshmallowPlugin(schema_name_resolver=resolver,),
+            ),
+        )
+        spec.components.schema('Pet', schema=PetSchema)
+        spec.path(
+            path='/pet',
+            operations={
+                'get': {
+                    'responses': {
+                        200: {
+                            'content': {
+                                'application/json': {
+                                    'schema': PetSchema,
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        )
+        get = get_paths(spec)['/pet']['get']
+        assert get['responses'][200]['content']['application/json']['schema']['$ref'] == ref_path(
+            spec,
+        ) + 'Pet'
+
     @pytest.mark.parametrize('spec_fixture', ('2.0', ), indirect=True)
     def test_schema_uses_ref_in_parameters_and_request_body_if_available_v2(self, spec_fixture):
         spec_fixture.spec.components.schema('Pet', schema=PetSchema)