Repository: marshmallow-code/apispec

Issue:
Deprecated flag is not configured correctly on query parameter objects
When using the `metadata` parameter on a Marshmallow field, the `deprecated` field is not properly configured on parameter objects with a location of `"query"` in the generated spec (the `description` field is set as expected, but not `deprecated`):

```python
from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from marshmallow import Schema, fields


expected_result = """paths:
  /endpoint:
    get:
      parameters:
      - in: query
        name: bar
        description: A field called bar
        deprecated: true
        schema:
          type: string
        required: true
info:
  title: My API Spec
  version: 1.0.0
openapi: 3.1.0
"""

actual_result = """paths:
  /endpoint:
    get:
      parameters:
      - in: query
        name: bar
        description: A field called bar
        schema:
          type: string
          deprecated: true
        required: true
info:
  title: My API Spec
  version: 1.0.0
openapi: 3.1.0
"""


class TestSchema(Schema):
    bar = fields.Str(
        required=True,
        metadata={"description": "A field called bar", "deprecated": True},
    )


spec = APISpec(
    title="My API Spec",
    version="1.0.0",
    openapi_version="3.1.0",
    plugins=[MarshmallowPlugin()],
)
spec.path(
    "/endpoint",
    operations={"get": {"parameters": [{"in": "query", "schema": TestSchema}]}},
)

assert spec.to_yaml() == actual_result  # passes when it should fail
assert spec.to_yaml() == expected_result  # fails when it should pass
```

The `deprecated` field works correctly for request and response bodies, but not for query parameters. It seems to me that setting `deprecated = True` in the metadata on a field instance should mark it as deprecated on the parameter object itself and not the nested schema object, since the schema itself is not what is deprecated.

It looks like the `OpenAPIConverter._field2parameter` method might need updating to promote the `deprecated` flag from the nested `schema` object into the parent object, similar to how `description` is handled.

Comments:

Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d6975b1..4376766 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,21 +1,21 @@
 repos:
 - repo: https://github.com/asottile/pyupgrade
-  rev: v3.8.0
+  rev: v3.10.1
   hooks:
   - id: pyupgrade
     args: [--py37-plus]
 - repo: https://github.com/psf/black
-  rev: 23.3.0
+  rev: 23.7.0
   hooks:
   - id: black
     language_version: python3
 - repo: https://github.com/pycqa/flake8
-  rev: 6.0.0
+  rev: 6.1.0
   hooks:
   - id: flake8
     additional_dependencies: [flake8-bugbear==22.12.6]
 - repo: https://github.com/asottile/blacken-docs
-  rev: 1.14.0
+  rev: 1.15.0
   hooks:
   - id: blacken-docs
     additional_dependencies: [black==22.3.0]
diff --git a/AUTHORS.rst b/AUTHORS.rst
index 02662ce..17bea2b 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -77,3 +77,4 @@ Contributors (chronological)
 - Edwin Erdmanis `@vorticity <https://github.com/vorticity>`_
 - Mounier Florian `@paradoxxxzero <https://github.com/paradoxxxzero>`_
 - Renato Damas `@codectl <https://github.com/codectl>`_
+- Tayler Sokalski `@tsokalski <https://github.com/tsokalski>`_
diff --git a/src/apispec/ext/marshmallow/field_converter.py b/src/apispec/ext/marshmallow/field_converter.py
index d075aca..2bf8a9d 100644
--- a/src/apispec/ext/marshmallow/field_converter.py
+++ b/src/apispec/ext/marshmallow/field_converter.py
@@ -6,6 +6,7 @@
     This module is treated as private API.
     Users should not need to use this module directly.
 """
+from __future__ import annotations
 import re
 import functools
 import operator
@@ -18,7 +19,7 @@ from marshmallow.orderedset import OrderedSet
 
 
 # marshmallow field => (JSON Schema type, format)
-DEFAULT_FIELD_MAPPING = {
+DEFAULT_FIELD_MAPPING: dict[type, tuple[str | None, str | None]] = {
     marshmallow.fields.Integer: ("integer", None),
     marshmallow.fields.Number: ("number", None),
     marshmallow.fields.Float: ("number", None),
@@ -86,7 +87,7 @@ _VALID_PREFIX = "x-"
 class FieldConverterMixin:
     """Adds methods for converting marshmallow fields to an OpenAPI properties."""
 
-    field_mapping = DEFAULT_FIELD_MAPPING
+    field_mapping: dict[type, tuple[str | None, str | None]] = DEFAULT_FIELD_MAPPING
     openapi_version: Version
 
     def init_attribute_functions(self):
diff --git a/src/apispec/ext/marshmallow/openapi.py b/src/apispec/ext/marshmallow/openapi.py
index a27bc4f..22ff6ef 100644
--- a/src/apispec/ext/marshmallow/openapi.py
+++ b/src/apispec/ext/marshmallow/openapi.py
@@ -197,6 +197,8 @@ class OpenAPIConverter(FieldConverterMixin):
         else:
             if "description" in prop:
                 ret["description"] = prop.pop("description")
+            if "deprecated" in prop:
+                ret["deprecated"] = prop.pop("deprecated")
             ret["schema"] = prop
 
         for param_attr_func in self.parameter_attribute_functions:


Test patch:
diff --git a/tests/test_ext_marshmallow_openapi.py b/tests/test_ext_marshmallow_openapi.py
index 2617329..ed7fb0e 100644
--- a/tests/test_ext_marshmallow_openapi.py
+++ b/tests/test_ext_marshmallow_openapi.py
@@ -245,6 +245,11 @@ class TestMarshmallowSchemaToParameters:
         res = openapi._field2parameter(field, name="field", location="query")
         assert res["required"] is True
 
+    def test_field_deprecated(self, openapi):
+        field = fields.Str(metadata={"deprecated": True})
+        res = openapi._field2parameter(field, name="field", location="query")
+        assert res["deprecated"] is True
+
     def test_schema_partial(self, openapi):
         class UserSchema(Schema):
             field = fields.Str(required=True)