Skip to content
Draft
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@
{% if method_settings is not none %}
{% for auto_populated_field in method_settings.auto_populated_fields %}
{% set is_proto3_optional = method.input.fields[auto_populated_field].proto3_optional %}
{% if is_async %}
self._client._setup_request_id(request, '{{ auto_populated_field }}', {{ is_proto3_optional }})
{% else %}
self._setup_request_id(request, '{{ auto_populated_field }}', {{ is_proto3_optional }})
{% endif %}
setup_request_id(request, '{{ auto_populated_field }}', {{ is_proto3_optional }})
{% endfor %}
{% endif %}{# if method_settings is not none #}
{% endwith %}{# method_settings #}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
{% import "%namespace/%name_%version/%sub/services/%service/_client_macros.j2" as macros %}
{% import "%namespace/%name_%version/%sub/services/%service/_shared_macros.j2" as shared_macros %}

{% set has_auto_populated_fields = namespace(value=false) %}
{% for method in service.methods.values() %}
{% set method_settings = api.all_method_settings.get(method.meta.address.proto) %}
{% if method_settings and method_settings.auto_populated_fields %}
{% set has_auto_populated_fields.value = true %}
{% endif %}
{% endfor %}

import logging as std_logging
from collections import OrderedDict
import re
from typing import Dict, Callable, Mapping, MutableMapping, MutableSequence, Optional, {% if service.any_server_streaming %}AsyncIterable, Awaitable, {% endif %}{% if service.any_client_streaming %}AsyncIterator, {% endif %}Sequence, Tuple, Type, Union
{% if api.all_method_settings.values()|map(attribute="auto_populated_fields", default=[])|select|list %}
import uuid
{% endif %}
{% if service.any_deprecated %}
import warnings
{% endif %}
Expand All @@ -21,6 +26,9 @@ from {{package_path}} import gapic_version as package_version
from google.api_core.client_options import ClientOptions
from google.api_core import exceptions as core_exceptions
from google.api_core import gapic_v1
{% if has_auto_populated_fields.value %}
from google.api_core.gapic_v1.request import setup_request_id
{% endif %}
from google.api_core import retry_async as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.oauth2 import service_account # type: ignore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
{% import "%namespace/%name_%version/%sub/services/%service/_client_macros.j2" as macros %}
{% import "%namespace/%name_%version/%sub/services/%service/_shared_macros.j2" as shared_macros %}

{% set has_auto_populated_fields = namespace(value=false) %}
{% for method in service.methods.values() %}
{% set method_settings = api.all_method_settings.get(method.meta.address.proto) %}
{% if method_settings and method_settings.auto_populated_fields %}
{% set has_auto_populated_fields.value = true %}
{% endif %}
{% endfor %}

from collections import OrderedDict
{% if service.any_extended_operations_methods %}
import functools
Expand All @@ -16,9 +24,6 @@ import logging as std_logging
import os
import re
from typing import Dict, Callable, Mapping, MutableMapping, MutableSequence, Optional, {% if service.any_server_streaming %}Iterable, {% endif %}{% if service.any_client_streaming %}Iterator, {% endif %}Sequence, Tuple, Type, Union, cast
{% if api.all_method_settings.values()|map(attribute="auto_populated_fields", default=[])|select|list %}
import uuid
{% endif %}
import warnings

{% set package_path = api.naming.module_namespace|join('.') + "." + api.naming.versioned_module_name %}
Expand All @@ -30,6 +35,9 @@ from google.api_core import exceptions as core_exceptions
from google.api_core import extended_operation
{% endif %}
from google.api_core import gapic_v1
{% if has_auto_populated_fields.value %}
from google.api_core.gapic_v1.request import setup_request_id
{% endif %}
from google.api_core import retry as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.auth.transport import mtls # type: ignore
Expand Down Expand Up @@ -453,37 +461,7 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
# NOTE (b/349488459): universe validation is disabled until further notice.
return True

{% if api.all_method_settings.values()|map(attribute="auto_populated_fields", default=[])|select|list %}
@staticmethod
def _setup_request_id(request, field_name: str, is_proto3_optional: bool):
"""Populate a UUID4 field in the request if it is not already set.

Args:
request (Union[google.protobuf.message.Message, dict]): The request object.
field_name (str): The name of the field to populate.
is_proto3_optional (bool): Whether the field is proto3 optional.
"""
if isinstance(request, dict):
if is_proto3_optional:
if field_name not in request:
request[field_name] = str(uuid.uuid4())
elif not request.get(field_name):
request[field_name] = str(uuid.uuid4())
return

if is_proto3_optional:
try:
# Pure protobuf messages
if not request.HasField(field_name):
setattr(request, field_name, str(uuid.uuid4()))
except (AttributeError, ValueError):
# Proto-plus messages or other objects
if field_name not in request:
setattr(request, field_name, str(uuid.uuid4()))
else:
if not getattr(request, field_name):
setattr(request, field_name, str(uuid.uuid4()))
{% endif %}

def _add_cred_info_for_auth_errors(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@
{% import "tests/unit/gapic/%name_%version/%sub/test_macros.j2" as test_macros %}
{% import "%namespace/%name_%version/%sub/services/%service/_shared_macros.j2" as shared_macros %}

{% set has_auto_populated_fields = namespace(value=false) %}
{% for method in service.methods.values() %}
{% set method_settings = api.all_method_settings.get(method.meta.address.proto) %}
{% if method_settings and method_settings.auto_populated_fields %}
{% set has_auto_populated_fields.value = true %}
{% endif %}
{% endfor %}

import os
import asyncio
{% if api.all_method_settings.values()|map(attribute="auto_populated_fields", default=[])|select|list %}
{% if has_auto_populated_fields.value %}
import re
{% endif %}
from unittest import mock
Expand Down Expand Up @@ -107,7 +115,7 @@ CRED_INFO_JSON = {
"principal": "service-account@example.com",
}
CRED_INFO_STRING = json.dumps(CRED_INFO_JSON)
{% if api.all_method_settings.values()|map(attribute="auto_populated_fields", default=[])|select|list %}
{% if has_auto_populated_fields.value %}
_UUID4_RE = re.compile(r"{{ uuid4_re }}")
{% endif %}

Expand Down Expand Up @@ -434,84 +442,6 @@ def test__add_cred_info_for_auth_errors_no_get_cred_info(error_code):
client._add_cred_info_for_auth_errors(error)
assert error.details == []

{% if api.all_method_settings.values()|map(attribute="auto_populated_fields", default=[])|select|list %}
def test__setup_request_id():
class MockRequest:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def __contains__(self, key):
return hasattr(self, key)

class MockProtoRequest:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def HasField(self, key):
return hasattr(self, key)

# Test with proto3 optional field not in request
request = MockRequest()
{{ service.client_name }}._setup_request_id(request, "request_id", True)
assert re.match(r"{{ test_macros.get_uuid4_re() }}", request.request_id)

# Test with proto3 optional field already in request
request = MockRequest(request_id="already_set")
{{ service.client_name }}._setup_request_id(request, "request_id", True)
assert request.request_id == "already_set"

# Test with non-proto3 optional field empty
request = MockRequest(request_id="")
{{ service.client_name }}._setup_request_id(request, "request_id", False)
assert re.match(r"{{ test_macros.get_uuid4_re() }}", request.request_id)

# Test with non-proto3 optional field already set
request = MockRequest(request_id="already_set")
{{ service.client_name }}._setup_request_id(request, "request_id", False)
assert request.request_id == "already_set"

# Test with proto3 optional field not in request (MockProtoRequest)
request = MockProtoRequest()
{{ service.client_name }}._setup_request_id(request, "request_id", True)
assert re.match(r"{{ test_macros.get_uuid4_re() }}", request.request_id)

# Test with proto3 optional field already in request (MockProtoRequest)
request = MockProtoRequest(request_id="already_set")
{{ service.client_name }}._setup_request_id(request, "request_id", True)
assert request.request_id == "already_set"

# Test with ValueError
class MockValueErrorRequest:
def HasField(self, key):
raise ValueError("Mismatched field")
def __contains__(self, key):
return hasattr(self, key)

request = MockValueErrorRequest()
{{ service.client_name }}._setup_request_id(request, "request_id", True)
assert re.match(r"{{ test_macros.get_uuid4_re() }}", request.request_id)

# Test with dict and proto3 optional field not in request
request = {}
{{ service.client_name }}._setup_request_id(request, "request_id", True)
assert re.match(r"{{ test_macros.get_uuid4_re() }}", request["request_id"])

# Test with dict and proto3 optional field already in request
request = {"request_id": "already_set"}
{{ service.client_name }}._setup_request_id(request, "request_id", True)
assert request["request_id"] == "already_set"

# Test with dict and non-proto3 optional field empty
request = {"request_id": ""}
{{ service.client_name }}._setup_request_id(request, "request_id", False)
assert re.match(r"{{ test_macros.get_uuid4_re() }}", request["request_id"])

# Test with dict and non-proto3 optional field already set
request = {"request_id": "already_set"}
{{ service.client_name }}._setup_request_id(request, "request_id", False)
assert request["request_id"] == "already_set"

{% endif %}
@pytest.mark.parametrize("client_class,transport_name", [
{% if 'grpc' in opts.transport %}
({{ service.client_name }}, "grpc"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from google.api_core import client_options as client_options_lib
from google.api_core import exceptions as core_exceptions
from google.api_core import gapic_v1
from google.api_core.gapic_v1 import request
from google.api_core import retry as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.auth.transport import mtls # type: ignore
Expand Down Expand Up @@ -480,26 +481,7 @@ def _setup_request_id(request, field_name: str, is_proto3_optional: bool):
field_name (str): The name of the field to populate.
is_proto3_optional (bool): Whether the field is proto3 optional.
"""
if isinstance(request, dict):
if is_proto3_optional:
if field_name not in request:
request[field_name] = str(uuid.uuid4())
elif not request.get(field_name):
request[field_name] = str(uuid.uuid4())
return

if is_proto3_optional:
try:
# Pure protobuf messages
if not request.HasField(field_name):
setattr(request, field_name, str(uuid.uuid4()))
except (AttributeError, ValueError):
# Proto-plus messages or other objects
if field_name not in request:
setattr(request, field_name, str(uuid.uuid4()))
else:
if not getattr(request, field_name):
setattr(request, field_name, str(uuid.uuid4()))
request.setup_request_id(request, field_name, is_proto3_optional)

def _add_cred_info_for_auth_errors(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,82 +351,6 @@ def test__add_cred_info_for_auth_errors_no_get_cred_info(error_code):
client._add_cred_info_for_auth_errors(error)
assert error.details == []

def test__setup_request_id():
class MockRequest:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def __contains__(self, key):
return hasattr(self, key)

class MockProtoRequest:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def HasField(self, key):
return hasattr(self, key)

# Test with proto3 optional field not in request
request = MockRequest()
StorageBatchOperationsClient._setup_request_id(request, "request_id", True)
assert re.match(r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}", request.request_id)

# Test with proto3 optional field already in request
request = MockRequest(request_id="already_set")
StorageBatchOperationsClient._setup_request_id(request, "request_id", True)
assert request.request_id == "already_set"

# Test with non-proto3 optional field empty
request = MockRequest(request_id="")
StorageBatchOperationsClient._setup_request_id(request, "request_id", False)
assert re.match(r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}", request.request_id)

# Test with non-proto3 optional field already set
request = MockRequest(request_id="already_set")
StorageBatchOperationsClient._setup_request_id(request, "request_id", False)
assert request.request_id == "already_set"

# Test with proto3 optional field not in request (MockProtoRequest)
request = MockProtoRequest()
StorageBatchOperationsClient._setup_request_id(request, "request_id", True)
assert re.match(r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}", request.request_id)

# Test with proto3 optional field already in request (MockProtoRequest)
request = MockProtoRequest(request_id="already_set")
StorageBatchOperationsClient._setup_request_id(request, "request_id", True)
assert request.request_id == "already_set"

# Test with ValueError
class MockValueErrorRequest:
def HasField(self, key):
raise ValueError("Mismatched field")
def __contains__(self, key):
return hasattr(self, key)

request = MockValueErrorRequest()
StorageBatchOperationsClient._setup_request_id(request, "request_id", True)
assert re.match(r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}", request.request_id)

# Test with dict and proto3 optional field not in request
request = {}
StorageBatchOperationsClient._setup_request_id(request, "request_id", True)
assert re.match(r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}", request["request_id"])

# Test with dict and proto3 optional field already in request
request = {"request_id": "already_set"}
StorageBatchOperationsClient._setup_request_id(request, "request_id", True)
assert request["request_id"] == "already_set"

# Test with dict and non-proto3 optional field empty
request = {"request_id": ""}
StorageBatchOperationsClient._setup_request_id(request, "request_id", False)
assert re.match(r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}", request["request_id"])

# Test with dict and non-proto3 optional field already set
request = {"request_id": "already_set"}
StorageBatchOperationsClient._setup_request_id(request, "request_id", False)
assert request["request_id"] == "already_set"

@pytest.mark.parametrize("client_class,transport_name", [
(StorageBatchOperationsClient, "grpc"),
(StorageBatchOperationsAsyncClient, "grpc_asyncio"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@
# Older Python versions safely ignore this variable.
__lazy_modules__: Set[str] = {
"google.api_core.gapic_v1.client_info",
"google.api_core.gapic_v1.request",
"google.api_core.gapic_v1.routing_header",
}
__all__ = ["client_info", "routing_header"]
__all__ = ["client_info", "request", "routing_header"]

if _has_grpc:
__lazy_modules__.update(
Expand All @@ -41,6 +42,7 @@

from google.api_core.gapic_v1 import ( # noqa: E402
client_info,
request,
routing_header,
)

Expand Down
Loading
Loading