Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.8k
chore(generator): move setup_request_id into compat layer#17739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
79f366862fbcc820adea6f03ac7c2cc27b59c2121cdbc73075f1b72199cee8e4ff191077f97c87fdf94e37ad06fb9e2c626bb65bc0482ae631ae5ec2001c792d1d2a59e169f85af52226ef6dd461aaabb0b2ceb23204cea3e6b78c1e6b56eea8d2638e9f505142d337e5f8e13905d2b17b7668c536a20ea8105ffe243bbd926eb3c22d569dc4eb2d9ebc6bd9eb08c77e052b6144e43ce6f0747882aa10c6eb89f7a25e3fb9770a1ee09ddece047593f813e560aa48cd4f6baaac5218d84709121dbd4f5f5b772917a22cc3f454c14fd175947fbb8f8022f2ad0dc81e34a279f2ee9cd9677a80a81919ee2e3b439f0e5ebcafa8fbae4b5087686352b4ddacf0d492bf6309bea02b7577b20055127d0b42c88dbac4cdd49e3930199d5dca70f564d49339ef25694ee805e0012d51adf2e8099f66c820af454430cf9d0433a86f8a5b9ec892809aa6c6b7File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -3,6 +3,7 @@ | ||
| {% block content %} | ||
| """A compatibility module for older versions of google-api-core.""" | ||
| {% set has_auto_populated_fields = api.all_method_settings.values()|map(attribute="auto_populated_fields", default=[])|select|list %} | ||
| {# TODO(https://github.com/googleapis/google-cloud-python/issues/17813): | ||
| Clean up this file/functions when the minimum supported version of | ||
| google-api-core has the functions in `_compat.py.j2`. #} | ||
| @@ -12,4 +13,55 @@ falling back to the local implementation if not present. #} | ||
| {# TODO(https://github.com/googleapis/google-cloud-python/issues/17883): | ||
| Backfill compatibility functions being removed from the client layer. #} | ||
| {% if has_auto_populated_fields %} | ||
| from typing import Union | ||
| import uuid | ||
| import google.protobuf.message | ||
| def setup_request_id( | ||
| request: Union[google.protobuf.message.Message, dict, None], | ||
| field_name: str, | ||
| is_proto3_optional: bool, | ||
| ) -> None: | ||
| """Populate a UUID4 field in the request if it is not already set. | ||
| This helper is used to ensure request idempotency by automatically | ||
| generating a unique identifier (such as `request_id`) for requests | ||
| that support it. If a request is retried, the same identifier can be | ||
| sent on subsequent retries, allowing the server to recognize the retried | ||
| request and prevent duplicate processing (e.g., creating duplicate | ||
| resources). | ||
| 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 or request[field_name] is None: | ||
| 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())) | ||
| {% endif %} | ||
| {% endblock %} | ||
hebaalazzeh marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -2,11 +2,101 @@ | ||
| {% block content %} | ||
| {% set has_auto_populated_fields = api.all_method_settings.values()|map(attribute="auto_populated_fields", default=[])|select|list %} | ||
| """Tests for the compatibility module for older versions of google-api-core.""" | ||
| {# TODO(https://github.com/googleapis/google-cloud-python/issues/17813): | ||
| Clean up this file/tests when the minimum supported version of | ||
| google-api-core has the functions in `_compat.py.j2`. #} | ||
| {# TODO(https://github.com/googleapis/google-cloud-python/issues/17883): | ||
| Backfill compatibility functions tests being removed from the client layer. #} | ||
hebaalazzeh marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| {% if has_auto_populated_fields %} | ||
| import re | ||
| import pytest | ||
| {% set package_path = api.naming.module_namespace|join('.') + "." + api.naming.versioned_module_name %} | ||
| from {{package_path}}._compat import 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) | ||
| class MockValueErrorRequest: | ||
| def HasField(self, key): | ||
| raise ValueError("Mismatched field") | ||
| def __contains__(self, key): | ||
| return hasattr(self, key) | ||
| UUID_REGEX = r"[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}" | ||
| @pytest.mark.parametrize( | ||
| "request_obj, is_proto3_optional, expected", | ||
| [ | ||
| (MockRequest(), True, "uuid"), | ||
| (MockRequest(request_id="already_set"), True, "already_set"), | ||
| (MockRequest(request_id=""), False, "uuid"), | ||
| (MockRequest(request_id="already_set"), False, "already_set"), | ||
| (MockProtoRequest(), True, "uuid"), | ||
| (MockProtoRequest(request_id="already_set"), True, "already_set"), | ||
| (MockValueErrorRequest(), True, "uuid"), | ||
| ({}, True, "uuid"), | ||
| ({"request_id": None}, True, "uuid"), | ||
| ({"request_id": "already_set"}, True, "already_set"), | ||
| ({"request_id": ""}, False, "uuid"), | ||
| ({"request_id": None}, False, "uuid"), | ||
| ({"request_id": "already_set"}, False, "already_set"), | ||
| (None, True, "none"), | ||
| ], | ||
| ids=[ | ||
| "proto3_optional_not_in_request", | ||
| "proto3_optional_already_in_request", | ||
| "non_proto3_optional_empty", | ||
| "non_proto3_optional_already_set", | ||
| "proto3_optional_not_in_request_proto", | ||
| "proto3_optional_already_in_request_proto", | ||
| "value_error_fallback", | ||
| "dict_proto3_optional_not_in_request", | ||
| "dict_proto3_optional_value_none", | ||
| "dict_proto3_optional_already_in_request", | ||
| "dict_non_proto3_optional_empty", | ||
| "dict_non_proto3_optional_value_none", | ||
| "dict_non_proto3_optional_already_set", | ||
| "none_request", | ||
| ], | ||
| ) | ||
| def test_setup_request_id(request_obj, is_proto3_optional, expected): | ||
| setup_request_id(request_obj, "request_id", is_proto3_optional) | ||
| if expected == "none": | ||
| assert request_obj is None | ||
| return | ||
| value = ( | ||
| request_obj["request_id"] | ||
| if isinstance(request_obj, dict) | ||
| else request_obj.request_id | ||
| ) | ||
| if expected == "uuid": | ||
| assert re.match(UUID_REGEX, value) | ||
| else: | ||
| assert value == expected | ||
| {% endif %} | ||
| {% endblock %} | ||
hebaalazzeh marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.