Repository: marshmallow-code/apispec

Issue:
Representing openapi's "Any Type"
I am trying to use `marshmallow.fields.Raw` to represent a "arbitrary json" field. Currently I do not see a way to represent a field in marshmallow that will produce a openapi property that is empty when passed through apispec.

The code in `apispec.marshmallow.openapi.OpenAPIConverter.field2type_and_format` (see https://github.com/marshmallow-code/apispec/blob/dev/apispec/ext/marshmallow/openapi.py#L159-L173) unconditionally sets the key `type` in the property object. However it seems that it is legal in openapi to have an empty schema (without the `type` key) which permits any type (see the "Any Type" section at the bottom of https://swagger.io/docs/specification/data-models/data-types/)

Please advise if this is possible with the current codebase or if I will need to implement a custom solution. Thanks.

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d30d8db..18cb18c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,9 @@ Changelog
 
 Features:
 
+- *Backwards-incompatible* Marshmallow ``fields.Raw`` and ``fields.Field`` are
+  now represented by OpenAPI `Any Type <https://swagger.io/docs/specification/data-models/data-types/#any>`_.
+  (:pr:`495`)
 - Add support for generating user defined OpenAPI properties for custom field
   classes via an ``add_attribute_function`` method. (:pr:`478`)
 - *Backwards-incompatible*: The ``schema_name_resolver`` function now receives
diff --git a/src/apispec/ext/marshmallow/field_converter.py b/src/apispec/ext/marshmallow/field_converter.py
index 1340ca3..8069eb3 100644
--- a/src/apispec/ext/marshmallow/field_converter.py
+++ b/src/apispec/ext/marshmallow/field_converter.py
@@ -37,9 +37,8 @@ DEFAULT_FIELD_MAPPING = {
     marshmallow.fields.Email: ("string", "email"),
     marshmallow.fields.URL: ("string", "url"),
     marshmallow.fields.Dict: ("object", None),
-    # Assume base Field and Raw are strings
-    marshmallow.fields.Field: ("string", None),
-    marshmallow.fields.Raw: ("string", None),
+    marshmallow.fields.Field: (None, None),
+    marshmallow.fields.Raw: (None, None),
     marshmallow.fields.List: ("array", None),
 }
 
@@ -183,8 +182,9 @@ class FieldConverterMixin:
             )
             type_, fmt = "string", None
 
-        ret = {"type": type_}
-
+        ret = {}
+        if type_:
+            ret["type"] = type_
         if fmt:
             ret["format"] = fmt
 
@@ -413,7 +413,6 @@ class FieldConverterMixin:
         :rtype: dict
         """
         if isinstance(field, marshmallow.fields.Nested):
-            del ret["type"]
             schema_dict = self.resolve_nested_schema(field.schema)
             if ret and "$ref" in schema_dict:
                 ret.update({"allOf": [schema_dict]})


Test patch:
diff --git a/tests/test_ext_marshmallow_field.py b/tests/test_ext_marshmallow_field.py
index dd520d4..63d9514 100644
--- a/tests/test_ext_marshmallow_field.py
+++ b/tests/test_ext_marshmallow_field.py
@@ -34,9 +34,6 @@ def test_field2choices_preserving_order(openapi):
         (fields.Time, "string"),
         (fields.Email, "string"),
         (fields.URL, "string"),
-        # Assume base Field and Raw are strings
-        (fields.Field, "string"),
-        (fields.Raw, "string"),
         # Custom fields inherit types from their parents
         (CustomStringField, "string"),
         (CustomIntegerField, "integer"),
@@ -48,6 +45,13 @@ def test_field2property_type(FieldClass, jsontype, spec_fixture):
     assert res["type"] == jsontype
 
 
+@pytest.mark.parametrize("FieldClass", [fields.Field, fields.Raw])
+def test_field2property_no_type_(FieldClass, spec_fixture):
+    field = FieldClass()
+    res = spec_fixture.openapi.field2property(field)
+    assert "type" not in res
+
+
 @pytest.mark.parametrize("ListClass", [fields.List, CustomList])
 def test_formatted_field_translates_to_array(ListClass, spec_fixture):
     field = ListClass(fields.String)