diff --git a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/base.py.j2 b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/base.py.j2 index 9c93aaea9c1d..4c64fcbc313e 100644 --- a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/base.py.j2 +++ b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/base.py.j2 @@ -146,6 +146,16 @@ class {{ service.name }}Transport(abc.ABC): # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest.py.j2 b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest.py.j2 index 4544b1ad7b1d..f38ebc2fd236 100644 --- a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest.py.j2 +++ b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest.py.j2 @@ -155,12 +155,12 @@ class {{service.name}}RestTransport(_Base{{ service.name }}RestTransport): custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -168,6 +168,7 @@ class {{service.name}}RestTransport(_Base{{ service.name }}RestTransport): client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) {% if service.has_lro %} @@ -212,6 +213,7 @@ class {{service.name}}RestTransport(_Base{{ service.name }}RestTransport): # use the credentials which are saved credentials=self._credentials, scopes=self._scopes, + client_cert_source_for_mtls=self._client_cert_source_for_mtls, http_options=http_options, path_prefix="{{ service.client_package_version }}") diff --git a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_base.py.j2 b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_base.py.j2 index 00c66d75a509..cfe4fb864814 100644 --- a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_base.py.j2 +++ b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_base.py.j2 @@ -57,6 +57,9 @@ class _Base{{ service.name }}RestTransport({{service.name}}Transport): def __init__(self, *, host: str{% if service.host %} = '{{ service.host }}'{% endif %}, credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -104,6 +107,9 @@ class _Base{{ service.name }}RestTransport({{service.name}}Transport): super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 b/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 index 136319c7ced0..46a6834adbaf 100644 --- a/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 +++ b/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 @@ -1432,6 +1432,18 @@ def test_{{ service.name|snake_case }}_base_transport_with_adc(): adc.assert_called_once() +def test_{{ service.name|snake_case }}_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.{{ service.name }}Transport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_{{ service.name|snake_case }}_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -1447,7 +1459,16 @@ def test_{{ service.name|snake_case }}_auth_adc(): ) -{% if 'grpc' in opts.transport %} +{% if 'grpc' in opts.transport and 'rest' in opts.transport %} +@pytest.mark.parametrize( + "transport_class", + [ + transports.{{ service.name }}GrpcTransport, + transports.{{ service.name }}GrpcAsyncIOTransport, + transports.{{ service.name }}RestTransport, + ], +) +{% elif 'grpc' in opts.transport %} @pytest.mark.parametrize( "transport_class", [ @@ -1455,6 +1476,14 @@ def test_{{ service.name|snake_case }}_auth_adc(): transports.{{ service.name }}GrpcAsyncIOTransport, ], ) +{% elif 'rest' in opts.transport %} +@pytest.mark.parametrize( + "transport_class", + [ + transports.{{ service.name }}RestTransport, + ], +) +{% endif %} def test_{{ service.name|snake_case }}_transport_auth_adc(transport_class): # If credentials and host are not provided, the transport class should use # ADC credentials. @@ -1511,6 +1540,7 @@ def test_{{ service.name|snake_case }}_transport_auth_gdch_credentials(transport ) +{% if 'grpc' in opts.transport %} @pytest.mark.parametrize( "transport_class,grpc_helpers", [ diff --git a/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/base.py b/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/base.py index 515c173224af..fabc875711b0 100755 --- a/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/base.py @@ -120,6 +120,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/rest.py index a2dde35054eb..ad57b937c17c 100755 --- a/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/rest.py @@ -1139,12 +1139,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -1152,6 +1152,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) self._operations_client: Optional[operations_v1.AbstractOperationsClient] = None @@ -1183,6 +1184,7 @@ def operations_client(self) -> operations_v1.AbstractOperationsClient: # use the credentials which are saved credentials=self._credentials, scopes=self._scopes, + client_cert_source_for_mtls=self._client_cert_source_for_mtls, http_options=http_options, path_prefix="v1") diff --git a/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/rest_base.py index d79cf2f07000..bc0430e603cc 100755 --- a/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/rest_base.py @@ -46,6 +46,9 @@ class _BaseAssetServiceRestTransport(AssetServiceTransport): def __init__(self, *, host: str = 'cloudasset.googleapis.com', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -88,6 +91,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py b/packages/gapic-generator/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py index 17fc4c8a5c07..324ce43fb448 100755 --- a/packages/gapic-generator/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py +++ b/packages/gapic-generator/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py @@ -17520,6 +17520,18 @@ def test_asset_service_base_transport_with_adc(): adc.assert_called_once() +def test_asset_service_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.AssetServiceTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_asset_service_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -17539,6 +17551,7 @@ def test_asset_service_auth_adc(): [ transports.AssetServiceGrpcTransport, transports.AssetServiceGrpcAsyncIOTransport, + transports.AssetServiceRestTransport, ], ) def test_asset_service_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/base.py b/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/base.py index 9ad2c974a10c..099f8dad82ca 100755 --- a/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/base.py @@ -117,6 +117,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/rest.py index fff3c34c4c26..84c6146bda9f 100755 --- a/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/rest.py @@ -352,12 +352,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -365,6 +365,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) if client_cert_source_for_mtls: diff --git a/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/rest_base.py index 3e11472082ab..20b55c829d9f 100755 --- a/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/rest_base.py @@ -44,6 +44,9 @@ class _BaseIAMCredentialsRestTransport(IAMCredentialsTransport): def __init__(self, *, host: str = 'iamcredentials.googleapis.com', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -86,6 +89,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py b/packages/gapic-generator/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py index f39f23ababfa..e5b609b8d9bd 100755 --- a/packages/gapic-generator/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py +++ b/packages/gapic-generator/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py @@ -3934,6 +3934,18 @@ def test_iam_credentials_base_transport_with_adc(): adc.assert_called_once() +def test_iam_credentials_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.IAMCredentialsTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_iam_credentials_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -3953,6 +3965,7 @@ def test_iam_credentials_auth_adc(): [ transports.IAMCredentialsGrpcTransport, transports.IAMCredentialsGrpcAsyncIOTransport, + transports.IAMCredentialsRestTransport, ], ) def test_iam_credentials_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/base.py b/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/base.py index 963fc7ddb134..55913f5b2fee 100755 --- a/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/base.py @@ -132,6 +132,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/rest.py index f25dd893e29e..4259ba471267 100755 --- a/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/rest.py @@ -2089,12 +2089,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -2102,6 +2102,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) self._operations_client: Optional[operations_v1.AbstractOperationsClient] = None @@ -2152,6 +2153,7 @@ def operations_client(self) -> operations_v1.AbstractOperationsClient: # use the credentials which are saved credentials=self._credentials, scopes=self._scopes, + client_cert_source_for_mtls=self._client_cert_source_for_mtls, http_options=http_options, path_prefix="v1") diff --git a/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/rest_base.py index 8ee51465cd79..ac92856fa8cd 100755 --- a/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/transports/rest_base.py @@ -58,6 +58,9 @@ class _BaseEventarcRestTransport(EventarcTransport): def __init__(self, *, host: str = 'eventarc.googleapis.com', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -100,6 +103,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py b/packages/gapic-generator/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py index b5e055c25ec9..9aa4d4f934cd 100755 --- a/packages/gapic-generator/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py +++ b/packages/gapic-generator/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py @@ -30961,6 +30961,18 @@ def test_eventarc_base_transport_with_adc(): adc.assert_called_once() +def test_eventarc_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.EventarcTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_eventarc_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -30980,6 +30992,7 @@ def test_eventarc_auth_adc(): [ transports.EventarcGrpcTransport, transports.EventarcGrpcAsyncIOTransport, + transports.EventarcRestTransport, ], ) def test_eventarc_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/transports/base.py b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/transports/base.py index 4a5dbf0b6ca1..37979f30942b 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/transports/base.py @@ -123,6 +123,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/transports/base.py b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/transports/base.py index 4f1882897e01..93119ede6b83 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/transports/base.py @@ -123,6 +123,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/transports/base.py b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/transports/base.py index 98e6b8826af6..d99ba11ed66d 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/transports/base.py @@ -123,6 +123,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py index acea90dd0a47..2bc0455cb8a7 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py @@ -12878,6 +12878,18 @@ def test_config_service_v2_base_transport_with_adc(): adc.assert_called_once() +def test_config_service_v2_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.ConfigServiceV2Transport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_config_service_v2_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: diff --git a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py index 433ff946bd8b..3f561e63bb4c 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py @@ -3470,6 +3470,18 @@ def test_logging_service_v2_base_transport_with_adc(): adc.assert_called_once() +def test_logging_service_v2_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.LoggingServiceV2Transport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_logging_service_v2_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: diff --git a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py index c3809e05816a..71bb163ef1fb 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py @@ -3270,6 +3270,18 @@ def test_metrics_service_v2_base_transport_with_adc(): adc.assert_called_once() +def test_metrics_service_v2_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.MetricsServiceV2Transport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_metrics_service_v2_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/transports/base.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/transports/base.py index 4a5dbf0b6ca1..37979f30942b 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/transports/base.py @@ -123,6 +123,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/transports/base.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/transports/base.py index 4f1882897e01..93119ede6b83 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/transports/base.py @@ -123,6 +123,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/transports/base.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/transports/base.py index 98e6b8826af6..d99ba11ed66d 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/transports/base.py @@ -123,6 +123,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_config_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_config_service_v2.py index 5d7d971258ae..e628e6797a07 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_config_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_config_service_v2.py @@ -12878,6 +12878,18 @@ def test_config_service_v2_base_transport_with_adc(): adc.assert_called_once() +def test_config_service_v2_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.ConfigServiceV2Transport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_config_service_v2_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_logging_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_logging_service_v2.py index 433ff946bd8b..3f561e63bb4c 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_logging_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_logging_service_v2.py @@ -3470,6 +3470,18 @@ def test_logging_service_v2_base_transport_with_adc(): adc.assert_called_once() +def test_logging_service_v2_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.LoggingServiceV2Transport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_logging_service_v2_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_metrics_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_metrics_service_v2.py index 5fc12c22406b..ba23d869bf62 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_metrics_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_metrics_service_v2.py @@ -3270,6 +3270,18 @@ def test_metrics_service_v2_base_transport_with_adc(): adc.assert_called_once() +def test_metrics_service_v2_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.MetricsServiceV2Transport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_metrics_service_v2_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: diff --git a/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/base.py b/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/base.py index 7164fc0bfe5f..cefee31c41d2 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/base.py @@ -120,6 +120,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest.py index 282ad08fec80..90d2909daabe 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest.py @@ -821,12 +821,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -834,6 +834,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) self._operations_client: Optional[operations_v1.AbstractOperationsClient] = None @@ -890,6 +891,7 @@ def operations_client(self) -> operations_v1.AbstractOperationsClient: # use the credentials which are saved credentials=self._credentials, scopes=self._scopes, + client_cert_source_for_mtls=self._client_cert_source_for_mtls, http_options=http_options, path_prefix="v1") diff --git a/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_base.py index eca2baf79f05..0b657f73959c 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_base.py @@ -46,6 +46,9 @@ class _BaseCloudRedisRestTransport(CloudRedisTransport): def __init__(self, *, host: str = 'redis.googleapis.com', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -88,6 +91,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py b/packages/gapic-generator/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py index e2215a295660..8aa522638fd5 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py +++ b/packages/gapic-generator/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py @@ -11540,6 +11540,18 @@ def test_cloud_redis_base_transport_with_adc(): adc.assert_called_once() +def test_cloud_redis_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.CloudRedisTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_cloud_redis_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -11559,6 +11571,7 @@ def test_cloud_redis_auth_adc(): [ transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport, + transports.CloudRedisRestTransport, ], ) def test_cloud_redis_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/base.py b/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/base.py index da37256c19e4..81dfc536ffa9 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/base.py @@ -120,6 +120,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/rest.py index 4543496d4cd9..97b3c726326e 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/rest.py @@ -557,12 +557,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -570,6 +570,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) self._operations_client: Optional[operations_v1.AbstractOperationsClient] = None @@ -626,6 +627,7 @@ def operations_client(self) -> operations_v1.AbstractOperationsClient: # use the credentials which are saved credentials=self._credentials, scopes=self._scopes, + client_cert_source_for_mtls=self._client_cert_source_for_mtls, http_options=http_options, path_prefix="v1") diff --git a/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/rest_base.py index d585f247733b..59e9385a404e 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/transports/rest_base.py @@ -46,6 +46,9 @@ class _BaseCloudRedisRestTransport(CloudRedisTransport): def __init__(self, *, host: str = 'redis.googleapis.com', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -88,6 +91,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/redis_selective/tests/unit/gapic/redis_v1/test_cloud_redis.py b/packages/gapic-generator/tests/integration/goldens/redis_selective/tests/unit/gapic/redis_v1/test_cloud_redis.py index 7b29386993ee..a3e8ecfca0e5 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis_selective/tests/unit/gapic/redis_v1/test_cloud_redis.py +++ b/packages/gapic-generator/tests/integration/goldens/redis_selective/tests/unit/gapic/redis_v1/test_cloud_redis.py @@ -6778,6 +6778,18 @@ def test_cloud_redis_base_transport_with_adc(): adc.assert_called_once() +def test_cloud_redis_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.CloudRedisTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_cloud_redis_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -6797,6 +6809,7 @@ def test_cloud_redis_auth_adc(): [ transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport, + transports.CloudRedisRestTransport, ], ) def test_cloud_redis_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/base.py index f5e34a9f9aa7..1777811aee9a 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/base.py @@ -121,6 +121,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/rest.py index 0608feae5957..ccbea470fc2e 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/rest.py @@ -760,12 +760,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -773,6 +773,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) self._operations_client: Optional[operations_v1.AbstractOperationsClient] = None @@ -822,6 +823,7 @@ def operations_client(self) -> operations_v1.AbstractOperationsClient: # use the credentials which are saved credentials=self._credentials, scopes=self._scopes, + client_cert_source_for_mtls=self._client_cert_source_for_mtls, http_options=http_options, path_prefix="v1beta1") diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/rest_base.py index fdcafa2f4288..d055aa3eaa1e 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/echo/transports/rest_base.py @@ -48,6 +48,9 @@ class _BaseEchoRestTransport(EchoTransport): def __init__(self, *, host: str = 'localhost:7469', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -90,6 +93,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/base.py index 8f44d529f6dd..a6750392c9dd 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/base.py @@ -121,6 +121,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/rest.py index 1ff8e157b8d8..7e1a7b1d5798 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/rest.py @@ -545,12 +545,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -558,6 +558,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) if client_cert_source_for_mtls: diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/rest_base.py index 2740986c9745..b2bec03b676f 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/identity/transports/rest_base.py @@ -49,6 +49,9 @@ class _BaseIdentityRestTransport(IdentityTransport): def __init__(self, *, host: str = 'localhost:7469', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -91,6 +94,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/base.py index c783a24ea331..795a6b0b2d51 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/base.py @@ -122,6 +122,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/rest.py index 0cfa8094cec7..80d40e9ddd48 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/rest.py @@ -825,12 +825,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -838,6 +838,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) self._operations_client: Optional[operations_v1.AbstractOperationsClient] = None @@ -887,6 +888,7 @@ def operations_client(self) -> operations_v1.AbstractOperationsClient: # use the credentials which are saved credentials=self._credentials, scopes=self._scopes, + client_cert_source_for_mtls=self._client_cert_source_for_mtls, http_options=http_options, path_prefix="v1beta1") diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/rest_base.py index fd5e21a2deb9..3696045ef22e 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/messaging/transports/rest_base.py @@ -49,6 +49,9 @@ class _BaseMessagingRestTransport(MessagingTransport): def __init__(self, *, host: str = 'localhost:7469', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -91,6 +94,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/base.py index b9ca386076b2..e319dffde3b6 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/base.py @@ -121,6 +121,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/rest.py index 40cdeb436e98..2629dcda6257 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/rest.py @@ -371,12 +371,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -384,6 +384,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) if client_cert_source_for_mtls: diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/rest_base.py index 7fe172ec6064..064da63faaf4 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/resumable_upload_service/transports/rest_base.py @@ -48,6 +48,9 @@ class _BaseResumableUploadServiceRestTransport(ResumableUploadServiceTransport): def __init__(self, *, host: str = 'localhost:7469', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -90,6 +93,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/base.py index f6b79c09fa5f..8a61fe447dd3 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/base.py @@ -122,6 +122,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/rest.py index 472dfeec16f1..e6d75b06b57e 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/rest.py @@ -592,12 +592,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -605,6 +605,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) if client_cert_source_for_mtls: diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/rest_base.py index eaf7eadf3eff..8b58ec2d2ec6 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/sequence_service/transports/rest_base.py @@ -50,6 +50,9 @@ class _BaseSequenceServiceRestTransport(SequenceServiceTransport): def __init__(self, *, host: str = 'localhost:7469', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -92,6 +95,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/base.py index f0fc08e3ef55..3d8dc13bc6a3 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/base.py @@ -121,6 +121,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/rest.py index 7aac1f262a8d..8f5314a713c0 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/rest.py @@ -651,12 +651,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -664,6 +664,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) if client_cert_source_for_mtls: diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/rest_base.py index f5592b41873f..bb671c54afc6 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/google/showcase_v1beta1/services/testing/transports/rest_base.py @@ -49,6 +49,9 @@ class _BaseTestingRestTransport(TestingTransport): def __init__(self, *, host: str = 'localhost:7469', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -91,6 +94,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_echo.py b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_echo.py index 526eb660d4ff..6b8650813c5c 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_echo.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_echo.py @@ -7640,6 +7640,18 @@ def test_echo_base_transport_with_adc(): adc.assert_called_once() +def test_echo_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.EchoTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_echo_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -7658,6 +7670,7 @@ def test_echo_auth_adc(): [ transports.EchoGrpcTransport, transports.EchoGrpcAsyncIOTransport, + transports.EchoRestTransport, ], ) def test_echo_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_identity.py b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_identity.py index 28a7dd420d23..6420a62c6315 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_identity.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_identity.py @@ -4782,6 +4782,18 @@ def test_identity_base_transport_with_adc(): adc.assert_called_once() +def test_identity_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.IdentityTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_identity_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -4800,6 +4812,7 @@ def test_identity_auth_adc(): [ transports.IdentityGrpcTransport, transports.IdentityGrpcAsyncIOTransport, + transports.IdentityRestTransport, ], ) def test_identity_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_messaging.py b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_messaging.py index 84906c414aa2..3bec7a409b8c 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_messaging.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_messaging.py @@ -9583,6 +9583,18 @@ def test_messaging_base_transport_with_adc(): adc.assert_called_once() +def test_messaging_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.MessagingTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_messaging_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -9601,6 +9613,7 @@ def test_messaging_auth_adc(): [ transports.MessagingGrpcTransport, transports.MessagingGrpcAsyncIOTransport, + transports.MessagingRestTransport, ], ) def test_messaging_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_resumable_upload_service.py b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_resumable_upload_service.py index 3d2f99ea1ef7..89618645bfa7 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_resumable_upload_service.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_resumable_upload_service.py @@ -2067,6 +2067,18 @@ def test_resumable_upload_service_base_transport_with_adc(): adc.assert_called_once() +def test_resumable_upload_service_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.ResumableUploadServiceTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_resumable_upload_service_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -2085,6 +2097,7 @@ def test_resumable_upload_service_auth_adc(): [ transports.ResumableUploadServiceGrpcTransport, transports.ResumableUploadServiceGrpcAsyncIOTransport, + transports.ResumableUploadServiceRestTransport, ], ) def test_resumable_upload_service_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_sequence_service.py b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_sequence_service.py index 444a6f91969a..9d9475071a09 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_sequence_service.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_sequence_service.py @@ -5410,6 +5410,18 @@ def test_sequence_service_base_transport_with_adc(): adc.assert_called_once() +def test_sequence_service_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.SequenceServiceTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_sequence_service_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -5428,6 +5440,7 @@ def test_sequence_service_auth_adc(): [ transports.SequenceServiceGrpcTransport, transports.SequenceServiceGrpcAsyncIOTransport, + transports.SequenceServiceRestTransport, ], ) def test_sequence_service_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_testing.py b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_testing.py index dbda59a14f09..508f65f56d74 100755 --- a/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_testing.py +++ b/packages/gapic-generator/tests/integration/goldens/showcase/tests/unit/gapic/showcase_v1beta1/test_testing.py @@ -5604,6 +5604,18 @@ def test_testing_base_transport_with_adc(): adc.assert_called_once() +def test_testing_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.TestingTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_testing_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -5622,6 +5634,7 @@ def test_testing_auth_adc(): [ transports.TestingGrpcTransport, transports.TestingGrpcAsyncIOTransport, + transports.TestingRestTransport, ], ) def test_testing_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/base.py b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/base.py index 5cf929454825..c9c80626bbe1 100755 --- a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/base.py +++ b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/base.py @@ -122,6 +122,16 @@ def __init__( # Don't apply audience if the credentials file passed from user. if hasattr(credentials, "with_gdch_audience"): credentials = credentials.with_gdch_audience(api_audience if api_audience else host) + else: + credentials = ga_credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=self.AUTH_SCOPES, + ) + if quota_project_id and isinstance( + credentials, ga_credentials.CredentialsWithQuotaProject + ): + credentials = credentials.with_quota_project(quota_project_id) # If the credentials are service account credentials, then always try to use self signed JWT. if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"): diff --git a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/rest.py b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/rest.py index f1f797714c66..fb06aa655a1c 100755 --- a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/rest.py +++ b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/rest.py @@ -578,12 +578,12 @@ def __init__(self, *, custom OpenTelemetry tracer providers. """ # Run the base constructor - # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. - # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the - # credentials object super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, url_scheme=url_scheme, @@ -591,6 +591,7 @@ def __init__(self, *, client_options=client_options, **kwargs, ) + self._client_cert_source_for_mtls = client_cert_source_for_mtls self._session = AuthorizedSession( self._credentials, default_host=self.DEFAULT_HOST) self._operations_client: Optional[operations_v1.AbstractOperationsClient] = None @@ -641,6 +642,7 @@ def operations_client(self) -> operations_v1.AbstractOperationsClient: # use the credentials which are saved credentials=self._credentials, scopes=self._scopes, + client_cert_source_for_mtls=self._client_cert_source_for_mtls, http_options=http_options, path_prefix="v1") diff --git a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/rest_base.py b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/rest_base.py index 9aa639cef740..43015f8f85a7 100755 --- a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/rest_base.py +++ b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/transports/rest_base.py @@ -48,6 +48,9 @@ class _BaseStorageBatchOperationsRestTransport(StorageBatchOperationsTransport): def __init__(self, *, host: str = 'storagebatchoperations.googleapis.com', credentials: Optional[Any] = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, always_use_jwt_access: Optional[bool] = False, url_scheme: str = 'https', @@ -90,6 +93,9 @@ def __init__(self, *, super().__init__( host=host, credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, client_info=client_info, always_use_jwt_access=always_use_jwt_access, api_audience=api_audience, diff --git a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/tests/unit/gapic/storagebatchoperations_v1/test_storage_batch_operations.py b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/tests/unit/gapic/storagebatchoperations_v1/test_storage_batch_operations.py index c1f5ed51ac5c..5b64b731af7c 100755 --- a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/tests/unit/gapic/storagebatchoperations_v1/test_storage_batch_operations.py +++ b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/tests/unit/gapic/storagebatchoperations_v1/test_storage_batch_operations.py @@ -6843,6 +6843,18 @@ def test_storage_batch_operations_base_transport_with_adc(): adc.assert_called_once() +def test_storage_batch_operations_base_transport_with_credentials(): + # Test the explicit credentials are scoped and assigned quota_project_id if provided. + creds = service_account.Credentials(None, None, None) + transport = transports.StorageBatchOperationsTransport( + credentials=creds, + quota_project_id="octopus", + scopes=["1", "2"], + ) + assert transport._credentials.quota_project_id == "octopus" + assert transport._credentials.scopes == ["1", "2"] + + def test_storage_batch_operations_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(google.auth, 'default', autospec=True) as adc: @@ -6862,6 +6874,7 @@ def test_storage_batch_operations_auth_adc(): [ transports.StorageBatchOperationsGrpcTransport, transports.StorageBatchOperationsGrpcAsyncIOTransport, + transports.StorageBatchOperationsRestTransport, ], ) def test_storage_batch_operations_transport_auth_adc(transport_class): diff --git a/packages/gapic-generator/tests/unit/generator/test_rest_transport_auth.py b/packages/gapic-generator/tests/unit/generator/test_rest_transport_auth.py new file mode 100644 index 000000000000..8c26b454746b --- /dev/null +++ b/packages/gapic-generator/tests/unit/generator/test_rest_transport_auth.py @@ -0,0 +1,372 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import ast +import dataclasses +import sys +import types +from unittest import mock + +from google.api import http_pb2 +from google.api import service_pb2 +import google.auth +from google.auth import credentials as ga_credentials +import jinja2 + +from gapic import utils +from gapic.samplegen_utils.utils import coerce_response_name, render_format_string +from gapic.schema import api +from gapic.schema import metadata +from gapic.schema import naming +from gapic.utils import Options +from test_utils.test_utils import get_method, make_service + + +def _make_jinja_env() -> jinja2.Environment: + opts = Options.build("transport=grpc+rest") + env = jinja2.Environment( + loader=jinja2.FileSystemLoader(searchpath=opts.templates), + undefined=jinja2.StrictUndefined, + extensions=["jinja2.ext.do"], + trim_blocks=True, + lstrip_blocks=True, + ) + env.filters["rst"] = utils.rst + if hasattr(utils, "make_private"): + env.filters["make_private"] = utils.make_private + env.filters["snake_case"] = utils.to_snake_case + env.filters["camel_case"] = utils.to_camel_case + env.filters["sort_lines"] = utils.sort_lines + env.filters["wrap"] = utils.wrap + env.filters["coerce_response_name"] = coerce_response_name + env.filters["render_format_string"] = render_format_string + env.tests["str_field_pb"] = utils.is_str_field_pb + env.tests["msg_field_pb"] = utils.is_msg_field_pb + if hasattr(utils, "UUID4_RE"): + env.globals["uuid4_re"] = utils.UUID4_RE + return env + + +def _build_api_and_service(has_lro: bool): + api_naming = naming.NewNaming( + namespace=("Google", "Cloud"), + name="Thingdoer", + version="v1", + proto_package="google.cloud.thingdoer.v1", + ) + service_yaml = service_pb2.Service( + http=http_pb2.Http( + rules=[ + http_pb2.HttpRule( + selector="google.longrunning.Operations.GetOperation", + get="/v1/{name=projects/*/operations/*}", + ) + ] + ) + ) + api_schema = api.API( + naming=api_naming, + all_protos={}, + service_yaml_config=service_yaml, + ) + + http_rule = http_pb2.HttpRule(post="/v1/things:do", body="*") + if has_lro: + method = get_method( + "DoThing", + "foo.bar.ThingRequest", + "google.longrunning.operations_pb2.Operation", + lro_response_type="foo.bar.ThingResponse", + lro_metadata_type="foo.bar.ThingMetadata", + http_rule=http_rule, + ) + else: + method = get_method( + "DoThing", + "foo.bar.ThingRequest", + "foo.bar.ThingResponse", + http_rule=http_rule, + ) + service = dataclasses.replace( + make_service( + name="ThingDoer", + host="thingdoer.googleapis.com", + scopes=("https://www.googleapis.com/auth/cloud-platform",), + methods=(method,), + ), + meta=metadata.Metadata( + address=metadata.Address( + name="ThingDoer", + package=("google", "cloud", "thingdoer", "v1"), + module="thingdoer", + api_naming=api_naming, + ) + ), + ) + return api_schema, service + + +def _render_and_exec_transports(has_lro: bool = False): + env = _make_jinja_env() + opts = Options.build("transport=grpc+rest") + api_schema, service = _build_api_and_service(has_lro=has_lro) + + compat_tpl = "%namespace/%name_%version/%sub/_compat.py.j2" + compat_code = ( + env.get_template(compat_tpl).render(api=api_schema, opts=opts) + if compat_tpl in env.loader.list_templates() + else None + ) + base_code = env.get_template( + "%namespace/%name_%version/%sub/services/%service/transports/base.py.j2" + ).render(api=api_schema, service=service, opts=opts) + rest_base_code = env.get_template( + "%namespace/%name_%version/%sub/services/%service/transports/rest_base.py.j2" + ).render(api=api_schema, service=service, opts=opts) + rest_code = env.get_template( + "%namespace/%name_%version/%sub/services/%service/transports/rest.py.j2" + ).render(api=api_schema, service=service, opts=opts) + + pkg_names = [ + "foo", + "foo.bar", + "google.cloud.thingdoer_v1", + "google.cloud.thingdoer_v1.services", + "google.cloud.thingdoer_v1.services.thing_doer", + "google.cloud.thingdoer_v1.services.thing_doer.transports", + ] + mod_keys = [ + "google.cloud.thingdoer_v1._compat", + "google.cloud.thingdoer_v1.services.thing_doer.transports.base", + "google.cloud.thingdoer_v1.services.thing_doer.transports.rest_base", + "google.cloud.thingdoer_v1.services.thing_doer.transports.rest", + ] + saved = {name: sys.modules.get(name) for name in pkg_names + mod_keys} + gcloud_pkg = sys.modules.get("google.cloud") + had_gcloud_attr = ( + hasattr(gcloud_pkg, "thingdoer_v1") if gcloud_pkg is not None else False + ) + saved_longrunning = sys.modules.get("google.longrunning") + saved_ops_pb2 = sys.modules.get("google.longrunning.operations_pb2") + saved_google_lr_attr = getattr(sys.modules["google"], "longrunning", None) + + try: + for name in pkg_names: + mod = types.ModuleType(name) + mod.__path__ = [] + sys.modules[name] = mod + + if gcloud_pkg is not None: + gcloud_pkg.thingdoer_v1 = sys.modules["google.cloud.thingdoer_v1"] + + class _DummyMsg: + pass + + if saved_longrunning is None: + lr_mod = types.ModuleType("google.longrunning") + lr_mod.__path__ = [] + ops_pb2_mod = types.ModuleType("google.longrunning.operations_pb2") + ops_pb2_mod.Operation = _DummyMsg + lr_mod.operations_pb2 = ops_pb2_mod + sys.modules["google.longrunning"] = lr_mod + sys.modules["google.longrunning.operations_pb2"] = ops_pb2_mod + sys.modules["google"].longrunning = lr_mod + + sys.modules["foo.bar"].ThingRequest = _DummyMsg + sys.modules["foo.bar"].ThingResponse = _DummyMsg + sys.modules["foo.bar"].ThingMetadata = _DummyMsg + sys.modules["foo"].bar = sys.modules["foo.bar"] + + sys.modules["google.cloud.thingdoer_v1"].gapic_version = types.SimpleNamespace( + __version__="0.1.0" + ) + sys.modules["google.cloud.thingdoer_v1"].services = sys.modules[ + "google.cloud.thingdoer_v1.services" + ] + sys.modules["google.cloud.thingdoer_v1.services"].thing_doer = sys.modules[ + "google.cloud.thingdoer_v1.services.thing_doer" + ] + sys.modules[ + "google.cloud.thingdoer_v1.services.thing_doer" + ].transports = sys.modules[ + "google.cloud.thingdoer_v1.services.thing_doer.transports" + ] + + if compat_code is not None: + compat_mod = types.ModuleType("google.cloud.thingdoer_v1._compat") + compat_mod.__package__ = "google.cloud.thingdoer_v1" + sys.modules[compat_mod.__name__] = compat_mod + sys.modules["google.cloud.thingdoer_v1"]._compat = compat_mod + exec(compat_code, compat_mod.__dict__) + + base_mod = types.ModuleType( + "google.cloud.thingdoer_v1.services.thing_doer.transports.base" + ) + base_mod.__package__ = ( + "google.cloud.thingdoer_v1.services.thing_doer.transports" + ) + sys.modules[base_mod.__name__] = base_mod + exec(base_code, base_mod.__dict__) + + rest_base_mod = types.ModuleType( + "google.cloud.thingdoer_v1.services.thing_doer.transports.rest_base" + ) + rest_base_mod.__package__ = ( + "google.cloud.thingdoer_v1.services.thing_doer.transports" + ) + sys.modules[rest_base_mod.__name__] = rest_base_mod + exec(rest_base_code, rest_base_mod.__dict__) + + rest_mod = types.ModuleType( + "google.cloud.thingdoer_v1.services.thing_doer.transports.rest" + ) + rest_mod.__package__ = ( + "google.cloud.thingdoer_v1.services.thing_doer.transports" + ) + sys.modules[rest_mod.__name__] = rest_mod + exec(rest_code, rest_mod.__dict__) + return base_mod, rest_mod + finally: + if saved_longrunning is None: + sys.modules.pop("google.longrunning", None) + if saved_ops_pb2 is None: + sys.modules.pop("google.longrunning.operations_pb2", None) + if saved_google_lr_attr is None and hasattr( + sys.modules["google"], "longrunning" + ): + delattr(sys.modules["google"], "longrunning") + if ( + gcloud_pkg is not None + and not had_gcloud_attr + and hasattr(gcloud_pkg, "thingdoer_v1") + ): + delattr(gcloud_pkg, "thingdoer_v1") + for name, orig in saved.items(): + if orig is None: + sys.modules.pop(name, None) + else: + sys.modules[name] = orig + + +def test_rest_transport_forwards_quota_scopes_and_credentials_file(): + _, rest_mod = _render_and_exec_transports() + rest_cls = rest_mod.ThingDoerRestTransport + + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + rest_cls(quota_project_id="billing-proj", scopes=["https://scope/a"]) + adc.assert_called_once_with( + scopes=["https://scope/a"], + default_scopes=("https://www.googleapis.com/auth/cloud-platform",), + quota_project_id="billing-proj", + ) + + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds: + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + rest_cls( + credentials_file="/tmp/creds.json", + quota_project_id="billing-proj", + scopes=["https://scope/b"], + ) + load_creds.assert_called_once_with( + "/tmp/creds.json", + scopes=["https://scope/b"], + default_scopes=("https://www.googleapis.com/auth/cloud-platform",), + quota_project_id="billing-proj", + ) + + +class _FakeScopedQuotaCredentials( + ga_credentials.Scoped, ga_credentials.CredentialsWithQuotaProject +): + def __init__(self, scopes=None, quota_project_id=None): + super().__init__() + self._scopes = scopes + self._quota_project_id = quota_project_id + + @property + def requires_scopes(self): + return not self._scopes + + def with_scopes(self, scopes, default_scopes=None): + return _FakeScopedQuotaCredentials( + scopes=scopes or default_scopes, + quota_project_id=self._quota_project_id, + ) + + def with_quota_project(self, quota_project_id): + return _FakeScopedQuotaCredentials( + scopes=self._scopes, + quota_project_id=quota_project_id, + ) + + def refresh(self, request): + self.token = "tok" + + +def test_base_and_rest_transport_apply_quota_and_scopes_to_explicit_credentials(): + _, rest_mod = _render_and_exec_transports() + rest_cls = rest_mod.ThingDoerRestTransport + + explicit = _FakeScopedQuotaCredentials() + transport = rest_cls( + credentials=explicit, + quota_project_id="quota-override", + scopes=["https://custom/scope"], + ) + assert transport._credentials._quota_project_id == "quota-override" + assert transport._credentials._scopes == ["https://custom/scope"] + + +def test_rest_transport_forwards_mtls_cert_to_operations_client(): + _, rest_mod = _render_and_exec_transports(has_lro=True) + rest_cls = rest_mod.ThingDoerRestTransport + + cert_cb = lambda: (b"cert", b"key") + with mock.patch.object( + rest_mod.AuthorizedSession, "configure_mtls_channel" + ) as cfg_mtls, mock.patch.object( + rest_mod.operations_v1, "OperationsRestTransport" + ) as ops_rest_cls, mock.patch.object( + rest_mod.operations_v1, "AbstractOperationsClient" + ) as ops_client_cls: + transport = rest_cls( + credentials=ga_credentials.AnonymousCredentials(), + client_cert_source_for_mtls=cert_cb, + ) + cfg_mtls.assert_called_once_with(cert_cb) + _ = transport.operations_client + ops_rest_cls.assert_called_once() + assert ( + ops_rest_cls.call_args.kwargs["client_cert_source_for_mtls"] + is cert_cb + ) + ops_client_cls.assert_called_once() + + +def test_generated_service_test_template_parses_for_all_transports(): + env = _make_jinja_env() + test_tpl = env.get_template( + "tests/unit/gapic/%name_%version/%sub/test_%service.py.j2" + ) + api_schema, service = _build_api_and_service(has_lro=True) + for transport_opt in ("grpc+rest", "rest", "grpc"): + opts = Options.build(f"transport={transport_opt}") + rendered = test_tpl.render(api=api_schema, service=service, opts=opts) + ast.parse(rendered) + assert "def test_thing_doer_base_transport_with_credentials" in rendered + if "rest" in opts.transport: + assert "transports.ThingDoerRestTransport" in rendered