Skip to content

Commit 9ad98ca

Browse files
authored
fix: only set unset fields if they are query params (googleapis#1130)
1 parent 0f3b149 commit 9ad98ca

File tree

8 files changed

+48
-80
lines changed

8 files changed

+48
-80
lines changed

gapic/ads-templates/%namespace/%name/%version/%sub/services/%service/transports/rest.py.j2

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ class {{service.name}}RestTransport({{service.name}}Transport):
182182
{% if not (method.server_streaming or method.client_streaming) %}
183183
{% if method.input.required_fields %}
184184
__REQUIRED_FIELDS_DEFAULT_VALUES = {
185-
{% for req_field in method.input.required_fields if req_field.is_primitive %}
186-
"{{ req_field.name | camel_case }}" : {% if req_field.field_pb.type == 9 %}"{{req_field.field_pb.default_value }}"{% else %}{{ req_field.field_pb.default_value or 0 }}{% endif %},{# default is str #}
185+
{% for req_field in method.input.required_fields if req_field.is_primitive and req_field.name in method.query_params %}
186+
"{{ req_field.name | camel_case }}" : {% if req_field.field_pb.type == 9 %}"{{req_field.field_pb.default_value }}"{% else %}{{ req_field.type.python_type(req_field.field_pb.default_value or 0) }}{% endif %},{# default is str #}
187187
{% endfor %}
188188
}
189189

gapic/ads-templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -994,7 +994,7 @@ def test_{{ method_name }}_rest_required_fields(request_type={{ method.input.ide
994994
))
995995

996996
# verify fields with default values are dropped
997-
{% for req_field in method.input.required_fields if req_field.is_primitive %}
997+
{% for req_field in method.input.required_fields if req_field.is_primitive and req_field.name in method.query_params %}
998998
{% set field_name = req_field.name | camel_case %}
999999
assert "{{ field_name }}" not in jsonified_request
10001000
{% endfor %}
@@ -1003,23 +1003,32 @@ def test_{{ method_name }}_rest_required_fields(request_type={{ method.input.ide
10031003
jsonified_request.update(unset_fields)
10041004

10051005
# verify required fields with default values are now present
1006-
{% for req_field in method.input.required_fields if req_field.is_primitive %}
1006+
{% for req_field in method.input.required_fields if req_field.is_primitive and req_field.name in method.query_params %}
10071007
{% set field_name = req_field.name | camel_case %}
10081008
assert "{{ field_name }}" in jsonified_request
10091009
assert jsonified_request["{{ field_name }}"] == request_init["{{ req_field.name }}"]
10101010
{% endfor %}
10111011

1012-
{% for req_field in method.input.required_fields if req_field.is_primitive %}
1012+
{% for req_field in method.input.required_fields if req_field.is_primitive and req_field.name in method.query_params %}
10131013
{% set field_name = req_field.name | camel_case %}
10141014
{% set mock_value = req_field.primitive_mock_as_str() %}
1015+
{% if method.query_params %}
1016+
# Check that path parameters and body parameters are not mixing in.
1017+
assert not set(unset_fields) - set(({% for param in method.query_params %}"{{param|camel_case }}", {% endfor %}))
1018+
{% endif %}
10151019
jsonified_request["{{ field_name }}"] = {{ mock_value }}
10161020
{% endfor %}
10171021

10181022
unset_fields = transport_class(credentials=ga_credentials.AnonymousCredentials()).{{ method.name | snake_case }}._get_unset_required_fields(jsonified_request)
1023+
{% if method.query_params %}
1024+
# Check that path parameters and body parameters are not mixing in.
1025+
assert not set(unset_fields) - set(({% for param in method.query_params %}"{{param}}",
1026+
{% endfor %}))
1027+
{% endif %}
10191028
jsonified_request.update(unset_fields)
10201029

10211030
# verify required fields with non-default values are left alone
1022-
{% for req_field in method.input.required_fields if req_field.is_primitive %}
1031+
{% for req_field in method.input.required_fields if req_field.is_primitive and req_field.name in method.query_params %}
10231032
{% set field_name = req_field.name | camel_case %}
10241033
{% set mock_value = req_field.primitive_mock_as_str() %}
10251034
assert "{{ field_name }}" in jsonified_request
@@ -1080,7 +1089,7 @@ def test_{{ method_name }}_rest_required_fields(request_type={{ method.input.ide
10801089
{% endif %}
10811090

10821091
expected_params = [
1083-
{% for req_field in method.input.required_fields if req_field.is_primitive %}
1092+
{% for req_field in method.input.required_fields if req_field.is_primitive and req_field.name in method.query_params %}
10841093
(
10851094
"{{ req_field.name | camel_case }}",
10861095
{% if req_field.field_pb.type == 9 %}
@@ -1095,6 +1104,13 @@ def test_{{ method_name }}_rest_required_fields(request_type={{ method.input.ide
10951104
assert expected_params == actual_params
10961105

10971106

1107+
def test_{{ method_name }}_rest_unset_required_fields():
1108+
transport = transports.{{ service.rest_transport_name }}(credentials=ga_credentials.AnonymousCredentials)
1109+
1110+
unset_fields = transport.{{ method.name|snake_case }}._get_unset_required_fields({})
1111+
assert set(unset_fields) == (set(({% for param in method.query_params %}"{{ param|camel_case }}", {% endfor %})) & set(({% for param in method.input.required_fields %}"{{param.name|camel_case}}", {% endfor %})))
1112+
1113+
10981114
{% endif %}{# required_fields #}
10991115

11001116

gapic/schema/wrappers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1026,7 +1026,7 @@ def path_params(self) -> Sequence[str]:
10261026
if self.http_opt is None:
10271027
return []
10281028

1029-
pattern = r'\{(\w+)\}'
1029+
pattern = r'\{(\w+)(?:=.+?)?\}'
10301030
return re.findall(pattern, self.http_opt['url'])
10311031

10321032
@property

gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest.py.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ class {{service.name}}RestTransport({{service.name}}Transport):
182182
{% if not (method.server_streaming or method.client_streaming) %}
183183
{% if method.input.required_fields %}
184184
__REQUIRED_FIELDS_DEFAULT_VALUES = {
185-
{% for req_field in method.input.required_fields if req_field.is_primitive %}
185+
{% for req_field in method.input.required_fields if req_field.is_primitive and req_field.name in method.query_params %}
186186
"{{ req_field.name | camel_case }}" : {% if req_field.field_pb.type == 9 %}"{{req_field.field_pb.default_value }}"{% else %}{{ req_field.type.python_type(req_field.field_pb.default_value or 0) }}{% endif %},{# default is str #}
187187
{% endfor %}
188188
}

gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2

Lines changed: 14 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,65 +1287,8 @@ def test_{{ method_name }}_raw_page_lro():
12871287
{% endfor %} {# method in methods for grpc #}
12881288

12891289
{% for method in service.methods.values() if 'rest' in opts.transport %}{% with method_name = method.name|snake_case + "_unary" if method.operation_service else method.name|snake_case %}{% if method.http_options %}
1290-
{# TODO(kbandes): remove this if condition when streaming are supported. #}
1291-
{% if not (method.server_streaming or method.client_streaming) %}
1292-
@pytest.mark.parametrize("request_type", [
1293-
{{ method.input.ident }},
1294-
dict,
1295-
])
1296-
def test_{{ method_name }}_rest(request_type, transport: str = 'rest'):
1297-
client = {{ service.client_name }}(
1298-
credentials=ga_credentials.AnonymousCredentials(),
1299-
transport="rest",
1300-
)
1301-
# Send a request that will satisfy transcoding
1302-
request = {{ method.input.ident }}({{ method.http_options[0].sample_request(method) }})
1303-
{% if method.client_streaming %}
1304-
requests = [request]
1305-
{% endif %}
1306-
1307-
1308-
with mock.patch.object(type(client.transport._session), 'request') as req:
1309-
{% if method.void %}
1310-
return_value = None
1311-
{% elif method.lro %}
1312-
return_value = operations_pb2.Operation(name='operations/spam')
1313-
{% elif method.server_streaming %}
1314-
return_value = iter([{{ method.output.ident }}()])
1315-
{% else %}
1316-
return_value = {{ method.output.ident }}(
1317-
{% for field in method.output.fields.values() | rejectattr('message')%}
1318-
{% if not field.oneof or field.proto3_optional %}
1319-
{{ field.name }}={{ field.mock_value }},
1320-
{% endif %}{% endfor %}
1321-
{# This is a hack to only pick one field #}
1322-
{% for oneof_fields in method.output.oneof_fields().values() %}
1323-
{% with field = oneof_fields[0] %}
1324-
{{ field.name }}={{ field.mock_value }},
1325-
{% endwith %}
1326-
{% endfor %}
1327-
)
1328-
{% endif %}
1329-
req.return_value = Response()
1330-
req.return_value.status_code = 500
1331-
req.return_value.request = PreparedRequest()
1332-
{% if method.void %}
1333-
json_return_value = ''
1334-
{% else %}
1335-
json_return_value = {{ method.output.ident }}.to_json(return_value)
1336-
{% endif %}
1337-
req.return_value._content = json_return_value.encode("UTF-8")
1338-
with pytest.raises(core_exceptions.GoogleAPIError):
1339-
# We only care that the correct exception is raised when putting
1340-
# the request over the wire, so an empty request is fine.
1341-
{% if method.client_streaming %}
1342-
client.{{ method_name }}(iter([requests]))
1343-
{% else %}
1344-
client.{{ method_name }}(request)
1345-
{% endif %}
1346-
1347-
13481290
{# TODO(kbandes): remove this if condition when lro and streaming are supported. #}
1291+
{% if not (method.server_streaming or method.client_streaming) %}
13491292
@pytest.mark.parametrize("request_type", [
13501293
{{ method.input.ident }},
13511294
dict,
@@ -1458,7 +1401,7 @@ def test_{{ method_name }}_rest_required_fields(request_type={{ method.input.ide
14581401
))
14591402

14601403
# verify fields with default values are dropped
1461-
{% for req_field in method.input.required_fields if req_field.is_primitive %}
1404+
{% for req_field in method.input.required_fields if req_field.is_primitive and req_field.name in method.query_params %}
14621405
{% set field_name = req_field.name | camel_case %}
14631406
assert "{{ field_name }}" not in jsonified_request
14641407
{% endfor %}
@@ -1467,7 +1410,7 @@ def test_{{ method_name }}_rest_required_fields(request_type={{ method.input.ide
14671410
jsonified_request.update(unset_fields)
14681411

14691412
# verify required fields with default values are now present
1470-
{% for req_field in method.input.required_fields if req_field.is_primitive %}
1413+
{% for req_field in method.input.required_fields if req_field.is_primitive and req_field.name in method.query_params %}
14711414
{% set field_name = req_field.name | camel_case %}
14721415
assert "{{ field_name }}" in jsonified_request
14731416
assert jsonified_request["{{ field_name }}"] == request_init["{{ req_field.name }}"]
@@ -1480,6 +1423,10 @@ def test_{{ method_name }}_rest_required_fields(request_type={{ method.input.ide
14801423
{% endfor %}
14811424

14821425
unset_fields = transport_class(credentials=ga_credentials.AnonymousCredentials()).{{ method.name | snake_case }}._get_unset_required_fields(jsonified_request)
1426+
{% if method.query_params %}
1427+
# Check that path parameters and body parameters are not mixing in.
1428+
assert not set(unset_fields) - set(({% for param in method.query_params %}"{{param}}", {% endfor %}))
1429+
{% endif %}
14831430
jsonified_request.update(unset_fields)
14841431

14851432
# verify required fields with non-default values are left alone
@@ -1544,7 +1491,7 @@ def test_{{ method_name }}_rest_required_fields(request_type={{ method.input.ide
15441491
{% endif %}
15451492

15461493
expected_params = [
1547-
{% for req_field in method.input.required_fields if req_field.is_primitive %}
1494+
{% for req_field in method.input.required_fields if req_field.is_primitive and req_field.name in method.query_params %}
15481495
(
15491496
"{{ req_field.name | camel_case }}",
15501497
{% if req_field.field_pb.type == 9 %}
@@ -1559,6 +1506,12 @@ def test_{{ method_name }}_rest_required_fields(request_type={{ method.input.ide
15591506
assert expected_params == actual_params
15601507

15611508

1509+
def test_{{ method_name }}_rest_unset_required_fields():
1510+
transport = transports.{{ service.rest_transport_name }}(credentials=ga_credentials.AnonymousCredentials)
1511+
1512+
unset_fields = transport.{{ method.name|snake_case }}._get_unset_required_fields({})
1513+
assert set(unset_fields) == (set(({% for param in method.query_params %}"{{ param|camel_case }}", {% endfor %})) & set(({% for param in method.input.required_fields %}"{{ param.name|camel_case }}", {% endfor %})))
1514+
15621515
{% endif %}{# required_fields #}
15631516

15641517

noxfile.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,6 @@ def __call__(self, frag):
103103
f"--python_gapic_opt=transport=grpc+rest,python-gapic-templates={templates}{maybe_old_naming}",
104104
]
105105

106-
if self.use_ads_templates:
107-
session_args.extend([])
108-
109106
outputs.append(
110107
self.session.run(*session_args, str(frag), external=True, silent=True,)
111108
)
@@ -114,7 +111,6 @@ def __call__(self, frag):
114111
# Note: install into the tempdir to prevent issues
115112
# with running pip concurrently.
116113
self.session.install(tmp_dir, "-e", ".", "-t", tmp_dir, "-qqq")
117-
118114
# Run the fragment's generated unit tests.
119115
# Don't bother parallelizing them: we already parallelize
120116
# the fragments, and there usually aren't too many tests per fragment.

tests/fragments/test_multiple_required_fields.proto

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,13 @@ service MultipleRequiredFields {
3030
}
3131
}
3232

33-
message Description {
34-
string description = 1;
35-
}
36-
3733
message MethodRequest {
3834
string kingdom = 1 [(google.api.field_behavior) = REQUIRED];
3935
string phylum = 2 [(google.api.field_behavior) = REQUIRED];
40-
Description description = 3 [(google.api.field_behavior) = REQUIRED];
36+
string name = 3 [(google.api.field_behavior) = REQUIRED];
37+
int32 armor_class = 4 [(google.api.field_behavior) = REQUIRED];
4138
}
4239

43-
message MethodResponse{}
40+
message MethodResponse{
41+
string text = 1;
42+
}

tests/unit/schema/wrappers/test_method.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,10 @@ def test_method_path_params():
325325
method = make_method('DoSomething', http_rule=http_rule)
326326
assert method.path_params == ['project']
327327

328+
http_rule2 = http_pb2.HttpRule(post='/v1beta1/{name=rooms/*/blurbs/*}')
329+
method2 = make_method("DoSomething", http_rule=http_rule2)
330+
assert method2.path_params == ["name"]
331+
328332

329333
def test_method_path_params_no_http_rule():
330334
method = make_method('DoSomething')

0 commit comments

Comments
 (0)