chore: add `async_rest` extra for async rest dependencies (#701)

* fix: add extra for aiohttp

* improve error message

* with -> w; apply same change to prerelease_deps

* move error to try block

* address feedback

* 🦉 Updates from OwlBot post-processor

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

* update noxfile

---------

Co-authored-by: ohmayr <omairnaveed@ymail.com>
Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml
index 5980f82..2d1193d 100644
--- a/.github/workflows/unittest.yml
+++ b/.github/workflows/unittest.yml
@@ -11,7 +11,7 @@
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        option: ["", "_grpc_gcp", "_wo_grpc", "_with_prerelease_deps", "_with_auth_aio"]
+        option: ["", "_grpc_gcp", "_wo_grpc", "_w_prerelease_deps", "_w_async_rest_extra"]
         python:
           - "3.7"
           - "3.8"
diff --git a/google/api_core/rest_streaming_async.py b/google/api_core/rest_streaming_async.py
index 812854c..370c2b5 100644
--- a/google/api_core/rest_streaming_async.py
+++ b/google/api_core/rest_streaming_async.py
@@ -22,7 +22,9 @@
     import google.auth.aio.transport
 except ImportError as e:  # pragma: NO COVER
     raise ImportError(
-        "google-auth>=2.35.0 is required to use asynchronous rest streaming."
+        "`google-api-core[async_rest]` is required to use asynchronous rest streaming. "
+        "Install the `async_rest` extra of `google-api-core` using "
+        "`pip install google-api-core[async_rest]`."
     ) from e
 
 import google.protobuf.message
diff --git a/noxfile.py b/noxfile.py
index 3fc4a72..144e3e2 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -38,7 +38,8 @@
     "unit",
     "unit_grpc_gcp",
     "unit_wo_grpc",
-    "unit_with_auth_aio",
+    "unit_w_prerelease_deps",
+    "unit_w_async_rest_extra",
     "cover",
     "pytype",
     "mypy",
@@ -110,7 +111,7 @@
         session.install(*other_deps)
 
 
-def default(session, install_grpc=True, prerelease=False, install_auth_aio=False):
+def default(session, install_grpc=True, prerelease=False, install_async_rest=False):
     """Default unit test session.
 
     This is intended to be run **without** an interpreter set, so
@@ -129,25 +130,38 @@
         "pytest-xdist",
     )
 
-    constraints_dir = str(CURRENT_DIRECTORY / "testing")
+    install_extras = []
+    if install_grpc:
+        install_extras.append("grpc")
 
+    constraints_dir = str(CURRENT_DIRECTORY / "testing")
+    if install_async_rest:
+        install_extras.append("async_rest")
+        constraints_type = "async-rest-"
+    else:
+        constraints_type = ""
+
+    lib_with_extras = f".[{','.join(install_extras)}]" if len(install_extras) else "."
     if prerelease:
         install_prerelease_dependencies(
-            session, f"{constraints_dir}/constraints-{PYTHON_VERSIONS[0]}.txt"
+            session,
+            f"{constraints_dir}/constraints-{constraints_type}{PYTHON_VERSIONS[0]}.txt",
         )
         # This *must* be the last install command to get the package from source.
-        session.install("-e", ".", "--no-deps")
+        session.install("-e", lib_with_extras, "--no-deps")
     else:
+        constraints_file = (
+            f"{constraints_dir}/constraints-{constraints_type}{session.python}.txt"
+        )
+        # fall back to standard constraints file
+        if not pathlib.Path(constraints_file).exists():
+            constraints_file = f"{constraints_dir}/constraints-{session.python}.txt"
+
         session.install(
             "-e",
-            ".[grpc]" if install_grpc else ".",
+            lib_with_extras,
             "-c",
-            f"{constraints_dir}/constraints-{session.python}.txt",
-        )
-
-    if install_auth_aio:
-        session.install(
-            "google-auth @ git+https://git@github.com/googleapis/google-auth-library-python@8833ad6f92c3300d6645355994c7db2356bd30ad"
+            constraints_file,
         )
 
     # Print out package versions of dependencies
@@ -205,7 +219,7 @@
 
 
 @nox.session(python=PYTHON_VERSIONS)
-def unit_with_prerelease_deps(session):
+def unit_w_prerelease_deps(session):
     """Run the unit test suite."""
     default(session, prerelease=True)
 
@@ -236,9 +250,9 @@
 
 
 @nox.session(python=PYTHON_VERSIONS)
-def unit_with_auth_aio(session):
-    """Run the unit test suite with google.auth.aio installed"""
-    default(session, install_auth_aio=True)
+def unit_w_async_rest_extra(session):
+    """Run the unit test suite with the `async_rest` extra"""
+    default(session, install_async_rest=True)
 
 
 @nox.session(python=DEFAULT_PYTHON_VERSION)
@@ -261,7 +275,7 @@
     """Run type-checking."""
     # TODO(https://github.com/googleapis/python-api-core/issues/682):
     # Use the latest version of mypy instead of mypy<1.11.0
-    session.install(".[grpc]", "mypy<1.11.0")
+    session.install(".[grpc,async_rest]", "mypy<1.11.0")
     session.install(
         "types-setuptools",
         "types-requests",
diff --git a/setup.py b/setup.py
index a9e01f4..d3c2a2b 100644
--- a/setup.py
+++ b/setup.py
@@ -36,6 +36,9 @@
     "requests >= 2.18.0, < 3.0.0.dev0",
 ]
 extras = {
+    "async_rest": [
+        "google-auth[aiohttp] >= 2.35.0, < 3.0.dev0",
+    ],
     "grpc": [
         "grpcio >= 1.33.2, < 2.0dev",
         "grpcio >= 1.49.1, < 2.0dev; python_version>='3.11'",
diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt
index fcc9831..4ce1c89 100644
--- a/testing/constraints-3.7.txt
+++ b/testing/constraints-3.7.txt
@@ -9,7 +9,6 @@
 protobuf==3.19.5
 google-auth==2.14.1
 requests==2.18.0
-packaging==14.3
 grpcio==1.33.2
 grpcio-status==1.33.2
 grpcio-gcp==0.2.2
diff --git a/testing/constraints-async-rest-3.7.txt b/testing/constraints-async-rest-3.7.txt
new file mode 100644
index 0000000..7aedeb1
--- /dev/null
+++ b/testing/constraints-async-rest-3.7.txt
@@ -0,0 +1,17 @@
+# This constraints file is used to check that lower bounds
+# are correct in setup.py
+# List *all* library dependencies and extras in this file.
+# Pin the version to the lower bound.
+#
+# e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev",
+# Then this file should have foo==1.14.0
+googleapis-common-protos==1.56.2
+protobuf==3.19.5
+google-auth==2.35.0
+# from google-auth[aiohttp]
+aiohttp==3.6.2
+requests==2.20.0
+grpcio==1.33.2
+grpcio-status==1.33.2
+grpcio-gcp==0.2.2
+proto-plus==1.22.3
diff --git a/tests/asyncio/test_rest_streaming_async.py b/tests/asyncio/test_rest_streaming_async.py
index 35820de..da5b1c8 100644
--- a/tests/asyncio/test_rest_streaming_async.py
+++ b/tests/asyncio/test_rest_streaming_async.py
@@ -28,14 +28,9 @@
 
 try:
     from google.auth.aio.transport import Response
-
-    AUTH_AIO_INSTALLED = True
 except ImportError:
-    AUTH_AIO_INSTALLED = False
-
-if not AUTH_AIO_INSTALLED:  # pragma: NO COVER
     pytest.skip(
-        "google-auth>=2.35.0 is required to use asynchronous rest streaming.",
+        "google-api-core[async_rest] is required to test asynchronous rest streaming.",
         allow_module_level=True,
     )