Skip to content

feat: move request-id auto-population logic to gapic_v1 public helpers#17738

Open
hebaalazzeh wants to merge 1 commit into
mainfrom
feat/gapic-centralization-api-core-request-id
Open

feat: move request-id auto-population logic to gapic_v1 public helpers#17738
hebaalazzeh wants to merge 1 commit into
mainfrom
feat/gapic-centralization-api-core-request-id

Conversation

@hebaalazzeh

@hebaalazzeh hebaalazzeh commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Introduces method_helpers module containing setup_request_id helper. Exposes the module as public in gapic_v1.

The unit tests in test_method_helpers.py are now identical to the generator's original test template test_service.py.j2 (with the exception of our added None request test case).

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new helper module method_helpers containing a setup_request_id function to populate a UUID4 field in requests if not already set, supporting dicts, protobuf, and proto-plus messages. It also adds a session-scoped pytest fixture to isolate unit tests from workstation mTLS environments, updates a nox session configuration, and relaxes a timing assertion in test_bidi.py. As there are no review comments, no additional feedback is provided.

@hebaalazzeh hebaalazzeh marked this pull request as ready for review July 16, 2026 15:20
@hebaalazzeh hebaalazzeh requested a review from a team as a code owner July 16, 2026 15:20
@hebaalazzeh hebaalazzeh self-assigned this Jul 16, 2026
@hebaalazzeh hebaalazzeh requested a review from parthea July 16, 2026 15:20
@hebaalazzeh hebaalazzeh force-pushed the feat/gapic-centralization-api-core-request-id branch from a0bff77 to 613b392 Compare July 16, 2026 16:18
@hebaalazzeh

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new helper module method_helpers containing the setup_request_id function, which populates a UUID4 field in request objects or dictionaries if not already set, along with corresponding unit tests. The review feedback suggests adding a None check at the beginning of setup_request_id and using a default value in getattr to prevent potential AttributeErrors, as well as adding a test case to verify graceful handling of None requests.

Comment on lines +23 to +51
def setup_request_id(request: Any, field_name: str, is_proto3_optional: bool) -> None:
"""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 not getattr(request, field_name, None):
setattr(request, field_name, str(uuid.uuid4()))
else:
if not getattr(request, field_name):
setattr(request, field_name, str(uuid.uuid4()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To ensure robust defensive programming and prevent potential runtime errors, we should add a None check at the beginning of setup_request_id. Additionally, using getattr(request, field_name, None) instead of getattr(request, field_name) in the else block prevents an AttributeError if the attribute is completely missing from the object.

def setup_request_id(request: Any, field_name: str, is_proto3_optional: bool) -> None:
    """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 request is None:
        return

    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 not getattr(request, field_name, None):
                setattr(request, field_name, str(uuid.uuid4()))
    else:
        if not getattr(request, field_name, None):
            setattr(request, field_name, str(uuid.uuid4()))
References
  1. When processing optional parameters with a helper function, prefer placing the null or None check within the function body rather than at the call site to simplify the calling code and improve encapsulation.

Comment on lines +60 to +64
# test custom non-iterable object wrapper
req_obj3 = CustomRequestWrapper()
setup_request_id(req_obj3, "request_id", True)
assert req_obj3.request_id != ""
uuid.UUID(req_obj3.request_id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add a test case to verify that setup_request_id handles a None request gracefully without raising any exceptions.

Suggested change
# test custom non-iterable object wrapper
req_obj3 = CustomRequestWrapper()
setup_request_id(req_obj3, "request_id", True)
assert req_obj3.request_id != ""
uuid.UUID(req_obj3.request_id)
# test custom non-iterable object wrapper
req_obj3 = CustomRequestWrapper()
setup_request_id(req_obj3, "request_id", True)
assert req_obj3.request_id != ""
uuid.UUID(req_obj3.request_id)
# test None request
setup_request_id(None, "request_id", True)

@hebaalazzeh hebaalazzeh force-pushed the feat/gapic-centralization-api-core-request-id branch 2 times, most recently from 08dce4a to e02ee4b Compare July 16, 2026 16:27
…lic helpers

Introduces method_helpers module containing setup_request_id helper. Exposes the module as public in gapic_v1.
@hebaalazzeh hebaalazzeh force-pushed the feat/gapic-centralization-api-core-request-id branch from e02ee4b to db040bb Compare July 16, 2026 16:28
# limitations under the License.
#

"""Helpers for method requests."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a better docstring?

@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a better name for this file? We already have api_core.gapic_v1.method, so api_core.gapic_v1.method_helpers seems confusing.

Would this fit in with the other helpers in api_core.gapic_v1.method? Or one of the other existing files? (method.py describes itself as "Helpers for wrapping low-level gRPC methods with common functionality". I can't remember if that's how this method is used)

If not, maybe we could call this api_core.gapic_v1.request, since it seems to be for building requests? Are other similar methods coming later, or is this a one-off?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants