diff --git a/gapic/schema/wrappers.py b/gapic/schema/wrappers.py index ed95295212..9fcc0a77cc 100644 --- a/gapic/schema/wrappers.py +++ b/gapic/schema/wrappers.py @@ -110,9 +110,16 @@ def mock_value(self) -> str: sub = next(iter(self.type.fields.values())) answer = f'{self.type.ident}({sub.name}={sub.mock_value})' - # If this is a repeated field, then the mock answer should - # be a list. - if self.repeated: + if self.map: + # Maps are a special case beacuse they're represented internally as + # a list of a generated type with two fields: 'key' and 'value'. + answer = '{{{}: {}}}'.format( + self.type.fields["key"].mock_value, + self.type.fields["value"].mock_value, + ) + elif self.repeated: + # If this is a repeated field, then the mock answer should + # be a list. answer = f'[{answer}]' # Done; return the mock value. @@ -568,7 +575,6 @@ def field_headers(self) -> Sequence[str]: @utils.cached_property def flattened_fields(self) -> Mapping[str, Field]: """Return the signature defined for this method.""" - signatures = self.options.Extensions[client_pb2.method_signature] cross_pkg_request = self.input.ident.package != self.ident.package def filter_fields(sig): @@ -585,6 +591,7 @@ def filter_fields(sig): yield name, field + signatures = self.options.Extensions[client_pb2.method_signature] answer: Dict[str, Field] = collections.OrderedDict( name_and_field for sig in signatures diff --git a/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 b/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 index 980bc47695..735f700847 100644 --- a/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 +++ b/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 @@ -204,9 +204,11 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta): {% endif %} {# different request package #} {#- Vanilla python protobuf wrapper types cannot _set_ repeated fields #} - {%- for key, field in method.flattened_fields.items() if not(field.repeated and method.input.ident.package != method.ident.package) %} + {% if method.flattened_fields -%} # If we have keyword arguments corresponding to fields on the # request, apply these. + {% endif -%} + {%- for key, field in method.flattened_fields.items() if not(field.repeated and method.input.ident.package != method.ident.package) %} if {{ field.name }} is not None: request.{{ key }} = {{ field.name }} {%- endfor %} diff --git a/tests/unit/schema/wrappers/test_field.py b/tests/unit/schema/wrappers/test_field.py index ffea8f4ed1..8ddc74ebf4 100644 --- a/tests/unit/schema/wrappers/test_field.py +++ b/tests/unit/schema/wrappers/test_field.py @@ -173,6 +173,25 @@ def test_mock_value_repeated(): assert field.mock_value == "['foo_bar_value']" +def test_mock_value_map(): + entry_msg = make_message( + name='SquidEntry', + fields=( + make_field(name='key', type='TYPE_STRING'), + make_field(name='value', type='TYPE_STRING'), + ), + options=descriptor_pb2.MessageOptions(map_entry=True), + ) + field = make_field( + name='squids', + type_name='mollusc.SquidEntry', + message=entry_msg, + label=3, + type='TYPE_MESSAGE', + ) + assert field.mock_value == "{'key_value': 'value_value'}" + + def test_mock_value_enum(): values = [ descriptor_pb2.EnumValueDescriptorProto(name='UNSPECIFIED', number=0), @@ -227,4 +246,26 @@ def make_field(*, message=None, enum=None, **kwargs) -> wrappers.Field: if isinstance(kwargs['type'], str): kwargs['type'] = T.Value(kwargs['type']) field_pb = descriptor_pb2.FieldDescriptorProto(**kwargs) - return wrappers.Field(field_pb=field_pb, message=message, enum=enum) + field = wrappers.Field(field_pb=field_pb, message=message, enum=enum) + return field + + +def make_message( + name, package='foo.bar.v1', module='baz', fields=(), meta=None, options=None +) -> wrappers.MessageType: + message_pb = descriptor_pb2.DescriptorProto( + name=name, + field=[i.field_pb for i in fields], + options=options, + ) + return wrappers.MessageType( + message_pb=message_pb, + fields=collections.OrderedDict((i.name, i) for i in fields), + nested_messages={}, + nested_enums={}, + meta=meta or metadata.Metadata(address=metadata.Address( + name=name, + package=tuple(package.split('.')), + module=module, + )), + )