feat: Allow representing enums with their unqualified symbolic names in headers (#465)

* feat: Allow non-fully-qualified enums in routing headers

* Rename s/fully_qualified_enums/qualified_enums/g for correctness

* chore: minor tweaks

* chore: Temporary workaround for pytest in noxfile.

* Fix import order

* bring coverage to 100%

* lint

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* remove replacement in owlbot.py causing lint failure

Co-authored-by: Anthonios Partheniou <partheniou@google.com>
Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
diff --git a/google/api_core/gapic_v1/routing_header.py b/google/api_core/gapic_v1/routing_header.py
index a7bcb5a..28b13ab 100644
--- a/google/api_core/gapic_v1/routing_header.py
+++ b/google/api_core/gapic_v1/routing_header.py
@@ -20,21 +20,32 @@
 Generally, these headers are specified as gRPC metadata.
 """
 
+from enum import Enum
 from urllib.parse import urlencode
 
 ROUTING_METADATA_KEY = "x-goog-request-params"
 
 
-def to_routing_header(params):
+def to_routing_header(params, qualified_enums=True):
     """Returns a routing header string for the given request parameters.
 
     Args:
         params (Mapping[str, Any]): A dictionary containing the request
             parameters used for routing.
+        qualified_enums (bool): Whether to represent enum values
+            as their type-qualified symbol names instead of as their
+            unqualified symbol names.
 
     Returns:
         str: The routing header string.
+
     """
+    if not qualified_enums:
+        if isinstance(params, dict):
+            tuples = params.items()
+        else:
+            tuples = params
+        params = [(x[0], x[1].name) if isinstance(x[1], Enum) else x for x in tuples]
     return urlencode(
         params,
         # Per Google API policy (go/api-url-encoding), / is not encoded.
@@ -42,16 +53,19 @@
     )
 
 
-def to_grpc_metadata(params):
+def to_grpc_metadata(params, qualified_enums=True):
     """Returns the gRPC metadata containing the routing headers for the given
     request parameters.
 
     Args:
         params (Mapping[str, Any]): A dictionary containing the request
             parameters used for routing.
+        qualified_enums (bool): Whether to represent enum values
+            as their type-qualified symbol names instead of as their
+            unqualified symbol names.
 
     Returns:
         Tuple(str, str): The gRPC metadata containing the routing header key
             and value.
     """
-    return (ROUTING_METADATA_KEY, to_routing_header(params))
+    return (ROUTING_METADATA_KEY, to_routing_header(params, qualified_enums))
diff --git a/noxfile.py b/noxfile.py
index 9bc2d96..4dcae55 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -94,7 +94,6 @@
         CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
     )
 
-    # Install all test dependencies, then install this package in-place.
     session.install(
         "dataclasses",
         "mock",
diff --git a/owlbot.py b/owlbot.py
index 8a60b15..5a83032 100644
--- a/owlbot.py
+++ b/owlbot.py
@@ -48,8 +48,6 @@
 """,
 )
 
-s.replace(".github/workflows/lint.yml", "python-version: \"3.10\"", "python-version: \"3.7\"")
-
 python.configure_previous_major_version_branches()
 
 s.shell.run(["nox", "-s", "blacken"], hide_output=False)
diff --git a/tests/unit/gapic/test_routing_header.py b/tests/unit/gapic/test_routing_header.py
index 3037867..9d31eb3 100644
--- a/tests/unit/gapic/test_routing_header.py
+++ b/tests/unit/gapic/test_routing_header.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from enum import Enum
+
 import pytest
 
 try:
@@ -35,6 +37,35 @@
     assert value == "name=me/ep&book.read=1%262"
 
 
+def test_enum_fully_qualified():
+    class Message:
+        class Color(Enum):
+            RED = 1
+            GREEN = 2
+            BLUE = 3
+
+    params = [("color", Message.Color.RED)]
+    value = routing_header.to_routing_header(params)
+    assert value == "color=Color.RED"
+    value = routing_header.to_routing_header(params, qualified_enums=True)
+    assert value == "color=Color.RED"
+
+
+def test_enum_nonqualified():
+    class Message:
+        class Color(Enum):
+            RED = 1
+            GREEN = 2
+            BLUE = 3
+
+    params = [("color", Message.Color.RED), ("num", 5)]
+    value = routing_header.to_routing_header(params, qualified_enums=False)
+    assert value == "color=RED&num=5"
+    params = {"color": Message.Color.RED, "num": 5}
+    value = routing_header.to_routing_header(params, qualified_enums=False)
+    assert value == "color=RED&num=5"
+
+
 def test_to_grpc_metadata():
     params = [("name", "meep"), ("book.read", "1")]
     metadata = routing_header.to_grpc_metadata(params)