| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 81482ba commit 3af2bfe
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,5 @@ | |||
| 1 | + 3.10.0 | ||
| 2 | + 3.11.0 | ||
| 3 | + 3.12.0 | ||
| 4 | + 3.13.0 | ||
| 5 | + 3.14.0 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -26,9 +26,10 @@ | |||
| 26 | 26 | __lazy_modules__: Set[str] = { | |
| 27 | 27 | "google.api_core.gapic_v1.client_info", | |
| 28 | 28 | "google.api_core.gapic_v1.requests", | |
| 29 | + "google.api_core.gapic_v1.rest_transcoding", | ||
| 29 | 30 | "google.api_core.gapic_v1.routing_header", | |
| 30 | 31 | } | |
| 31 | - __all__ = ["client_info", "requests", "routing_header"] | ||
| 32 | + __all__ = ["client_info", "requests", "rest_transcoding", "routing_header"] | ||
| 32 | 33 | ||
| 33 | 34 | if _has_grpc: | |
| 34 | 35 | __lazy_modules__.update( | |
@@ -43,6 +44,7 @@ | |||
| 43 | 44 | from google.api_core.gapic_v1 import ( # noqa: E402 | |
| 44 | 45 | client_info, | |
| 45 | 46 | requests, | |
| 47 | + rest_transcoding, | ||
| 46 | 48 | routing_header, | |
| 47 | 49 | ) | |
| 48 | 50 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,8 +21,8 @@ | |||
| 21 | 21 | if they are not already set. | |
| 22 | 22 | """ | |
| 23 | 23 | ||
| 24 | - from typing import Union | ||
| 25 | 24 | import uuid | |
| 25 | + from typing import Union | ||
| 26 | 26 | ||
| 27 | 27 | import google.protobuf.message | |
| 28 | 28 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,73 @@ | |||
| 1 | + # Copyright 2026 Google LLC | ||
| 2 | + # | ||
| 3 | + # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + # you may not use this file except in compliance with the License. | ||
| 5 | + # You may obtain a copy of the License at | ||
| 6 | + # | ||
| 7 | + # http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + # | ||
| 9 | + # Unless required by applicable law or agreed to in writing, software | ||
| 10 | + # distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + # See the License for the specific language governing permissions and | ||
| 13 | + # limitations under the License. | ||
| 14 | + | ||
| 15 | + import json | ||
| 16 | + from typing import Any, Dict, List, Optional, Tuple | ||
| 17 | + | ||
| 18 | + from google.protobuf import json_format | ||
| 19 | + | ||
| 20 | + from google.api_core import path_template | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + def transcode( | ||
| 24 | + http_options: List[Dict[str, str]], | ||
| 25 | + request: Any, | ||
| 26 | + required_fields_default_values: Optional[Dict[str, Any]] = None, | ||
| 27 | + rest_numeric_enums: bool = False, | ||
| 28 | + ) -> Tuple[Dict[str, Any], Optional[str], Dict[str, Any]]: | ||
| 29 | + """Transcodes a request into HTTP method, URI, body, and query parameters. | ||
| 30 | + | ||
| 31 | + Args: | ||
| 32 | + http_options (List[Dict[str, str]]): List of HTTP transcoding rules. | ||
| 33 | + request (Any): The protobuf or proto-plus request message. | ||
| 34 | + required_fields_default_values (Optional[Dict[str, Any]]): Dictionary | ||
| 35 | + of required fields default values to merge into query parameters if missing. | ||
| 36 | + rest_numeric_enums (bool): Whether to encode enums as integers. | ||
| 37 | + | ||
| 38 | + Returns: | ||
| 39 | + Tuple[Dict[str, Any], Optional[str], Dict[str, Any]]: A tuple containing: | ||
| 40 | + - The raw transcoded request dictionary (containing keys like 'uri', 'method'). | ||
| 41 | + - The serialized request body JSON string, or None if no body. | ||
| 42 | + - The query parameters dictionary. | ||
| 43 | + """ | ||
| 44 | + # Convert proto-plus message to its underlying protobuf message if needed | ||
| 45 | + pb_request = getattr(request, "_pb", request) | ||
| 46 | + | ||
| 47 | + transcoded_request = path_template.transcode(http_options, pb_request) | ||
| 48 | + | ||
| 49 | + body_json = None | ||
| 50 | + if transcoded_request.get("body") is not None: | ||
| 51 | + body_json = json_format.MessageToJson( | ||
| 52 | + transcoded_request["body"], | ||
| 53 | + use_integers_for_enums=rest_numeric_enums, | ||
| 54 | + ) | ||
| 55 | + | ||
| 56 | + query_params_json = {} | ||
| 57 | + if transcoded_request.get("query_params") is not None: | ||
| 58 | + query_params_json = json.loads( | ||
| 59 | + json_format.MessageToJson( | ||
| 60 | + transcoded_request["query_params"], | ||
| 61 | + use_integers_for_enums=rest_numeric_enums, | ||
| 62 | + ) | ||
| 63 | + ) | ||
| 64 | + | ||
| 65 | + if required_fields_default_values: | ||
| 66 | + for k, v in required_fields_default_values.items(): | ||
| 67 | + if k not in query_params_json: | ||
| 68 | + query_params_json[k] = v | ||
| 69 | + | ||
| 70 | + if rest_numeric_enums: | ||
| 71 | + query_params_json["$alt"] = "json;enum-encoding=int" | ||
| 72 | + | ||
| 73 | + return transcoded_request, body_json, query_params_json | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,31 @@ | |||
| 1 | + # Copyright 2026 Google LLC | ||
| 2 | + # | ||
| 3 | + # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + # you may not use this file except in compliance with the License. | ||
| 5 | + # You may obtain a copy of the License at | ||
| 6 | + # | ||
| 7 | + # http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + # | ||
| 9 | + # Unless required by applicable law or agreed to in writing, software | ||
| 10 | + # distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + # See the License for the specific language governing permissions and | ||
| 13 | + # limitations under the License. | ||
| 14 | + | ||
| 15 | + import os | ||
| 16 | + from unittest import mock | ||
| 17 | + | ||
| 18 | + import pytest | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + @pytest.fixture(scope="session", autouse=True) | ||
| 22 | + def mock_mtls_env(): | ||
| 23 | + """Autouse session-scoped fixture to isolate unit tests from workstation mTLS environments.""" | ||
| 24 | + with mock.patch.dict( | ||
| 25 | + os.environ, | ||
| 26 | + { | ||
| 27 | + "GOOGLE_API_USE_CLIENT_CERTIFICATE": "false", | ||
| 28 | + "CLOUDSDK_CONTEXT_AWARE_USE_CLIENT_CERTIFICATE": "false", | ||
| 29 | + }, | ||
| 30 | + ): | ||
| 31 | + yield | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,7 +19,6 @@ | |||
| 19 | 19 | ||
| 20 | 20 | from google.api_core.gapic_v1.requests import setup_request_id | |
| 21 | 21 | ||
| 22 | - | ||
| 23 | 22 | # --- Mock Request Helper Classes --- | |
| 24 | 23 | ||
| 25 | 24 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,136 @@ | |||
| 1 | + # Copyright 2026 Google LLC | ||
| 2 | + # | ||
| 3 | + # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + # you may not use this file except in compliance with the License. | ||
| 5 | + # You may obtain a copy of the License at | ||
| 6 | + # | ||
| 7 | + # http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + # | ||
| 9 | + # Unless required by applicable law or agreed to in writing, software | ||
| 10 | + # distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + # See the License for the specific language governing permissions and | ||
| 13 | + # limitations under the License. | ||
| 14 | + | ||
| 15 | + import json | ||
| 16 | + | ||
| 17 | + from google.protobuf import descriptor_pb2 | ||
| 18 | + | ||
| 19 | + from google.api_core.gapic_v1.rest_transcoding import transcode | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + def test_transcode_basic(): | ||
| 23 | + # We use FieldDescriptorProto as it has standard primitive fields and nested messages. | ||
| 24 | + http_options = [ | ||
| 25 | + { | ||
| 26 | + "method": "get", | ||
| 27 | + "uri": "/v1/test/{name}", | ||
| 28 | + } | ||
| 29 | + ] | ||
| 30 | + | ||
| 31 | + request = descriptor_pb2.FieldDescriptorProto() | ||
| 32 | + request.name = "my-field" | ||
| 33 | + request.number = 123 | ||
| 34 | + | ||
| 35 | + transcoded, body, query_params = transcode(http_options, request) | ||
| 36 | + | ||
| 37 | + assert transcoded["method"] == "get" | ||
| 38 | + assert transcoded["uri"] == "/v1/test/my-field" | ||
| 39 | + assert body is None | ||
| 40 | + # 'number' should be in query parameters | ||
| 41 | + assert "number" in query_params | ||
| 42 | + assert query_params["number"] == 123 | ||
| 43 | + | ||
| 44 | + | ||
| 45 | + def test_transcode_with_nested_field(): | ||
| 46 | + http_options = [ | ||
| 47 | + { | ||
| 48 | + "method": "get", | ||
| 49 | + "uri": "/v1/test/{options.deprecated}/{name}", | ||
| 50 | + } | ||
| 51 | + ] | ||
| 52 | + | ||
| 53 | + request = descriptor_pb2.FieldDescriptorProto() | ||
| 54 | + request.name = "my-field" | ||
| 55 | + request.options.deprecated = True | ||
| 56 | + request.number = 123 | ||
| 57 | + | ||
| 58 | + transcoded, body, query_params = transcode(http_options, request) | ||
| 59 | + | ||
| 60 | + assert transcoded["method"] == "get" | ||
| 61 | + assert transcoded["uri"] == "/v1/test/True/my-field" | ||
| 62 | + assert body is None | ||
| 63 | + assert "number" in query_params | ||
| 64 | + assert query_params["number"] == 123 | ||
| 65 | + | ||
| 66 | + | ||
| 67 | + def test_transcode_with_body(): | ||
| 68 | + http_options = [ | ||
| 69 | + { | ||
| 70 | + "method": "post", | ||
| 71 | + "uri": "/v1/test/{name}", | ||
| 72 | + "body": "options", | ||
| 73 | + } | ||
| 74 | + ] | ||
| 75 | + | ||
| 76 | + request = descriptor_pb2.FieldDescriptorProto() | ||
| 77 | + request.name = "my-field" | ||
| 78 | + request.options.deprecated = True | ||
| 79 | + request.number = 123 | ||
| 80 | + | ||
| 81 | + transcoded, body, query_params = transcode(http_options, request) | ||
| 82 | + | ||
| 83 | + assert transcoded["method"] == "post" | ||
| 84 | + assert transcoded["uri"] == "/v1/test/my-field" | ||
| 85 | + assert body is not None | ||
| 86 | + body_data = json.loads(body) | ||
| 87 | + assert body_data["deprecated"] is True | ||
| 88 | + # Query parameters should not contain 'options' (the body) | ||
| 89 | + assert "number" in query_params | ||
| 90 | + assert query_params["number"] == 123 | ||
| 91 | + assert "options" not in query_params | ||
| 92 | + | ||
| 93 | + | ||
| 94 | + def test_transcode_with_required_fields_default_values(): | ||
| 95 | + http_options = [ | ||
| 96 | + { | ||
| 97 | + "method": "get", | ||
| 98 | + "uri": "/v1/test/{name}", | ||
| 99 | + } | ||
| 100 | + ] | ||
| 101 | + | ||
| 102 | + request = descriptor_pb2.FieldDescriptorProto() | ||
| 103 | + request.name = "my-field" | ||
| 104 | + | ||
| 105 | + required_defaults = {"requiredQueryParam": "default-val"} | ||
| 106 | + | ||
| 107 | + transcoded, body, query_params = transcode( | ||
| 108 | + http_options, | ||
| 109 | + request, | ||
| 110 | + required_fields_default_values=required_defaults, | ||
| 111 | + ) | ||
| 112 | + | ||
| 113 | + assert query_params["requiredQueryParam"] == "default-val" | ||
| 114 | + | ||
| 115 | + | ||
| 116 | + def test_transcode_with_numeric_enums(): | ||
| 117 | + http_options = [ | ||
| 118 | + { | ||
| 119 | + "method": "get", | ||
| 120 | + "uri": "/v1/test/{name}", | ||
| 121 | + } | ||
| 122 | + ] | ||
| 123 | + | ||
| 124 | + request = descriptor_pb2.FieldDescriptorProto() | ||
| 125 | + request.name = "my-field" | ||
| 126 | + request.type = descriptor_pb2.FieldDescriptorProto.TYPE_STRING | ||
| 127 | + | ||
| 128 | + # Without numeric enums | ||
| 129 | + _, _, query_params = transcode(http_options, request, rest_numeric_enums=False) | ||
| 130 | + assert query_params["type"] == "TYPE_STRING" | ||
| 131 | + | ||
| 132 | + # With numeric enums | ||
| 133 | + _, _, query_params = transcode(http_options, request, rest_numeric_enums=True) | ||
| 134 | + # Type number for TYPE_STRING is 9 | ||
| 135 | + assert query_params["type"] == 9 | ||
| 136 | + assert query_params["$alt"] == "json;enum-encoding=int" | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments