def test_method_paged_result_field_no_page_field(): input_msg = make_message( name='ListFoosRequest', fields=( make_field(name='parent', type=9), # str make_field(name='page_size', type=5), # int make_field(name='page_token', type=9), # str )) output_msg = make_message( name='ListFoosResponse', fields=( make_field(name='foos', message=make_message('Foo'), repeated=False), make_field(name='next_page_token', type=9), # str )) method = make_method( 'ListFoos', input_message=input_msg, output_message=output_msg, ) assert method.paged_result_field is None method = make_method( name='Foo', input_message=make_message( name='FooRequest', fields=(make_field(name='page_token', type=9), ) # str ), output_message=make_message( name='FooResponse', fields=(make_field(name='next_page_token', type=9), ) # str )) assert method.paged_result_field is None
def test_method_field_headers_present(): verbs = [ 'get', 'put', 'post', 'delete', 'patch', ] for v in verbs: rule = http_pb2.HttpRule(**{v: '/v1/{parent=projects/*}/topics'}) method = make_method('DoSomething', http_rule=rule) assert method.field_headers == (wrappers.FieldHeader('parent'), ) assert method.field_headers[0].raw == 'parent' assert method.field_headers[0].disambiguated == 'parent' # test that reserved keyword in field header is disambiguated rule = http_pb2.HttpRule(**{v: '/v1/{object=objects/*}/topics'}) method = make_method('DoSomething', http_rule=rule) assert method.field_headers == (wrappers.FieldHeader('object'), ) assert method.field_headers[0].raw == 'object' assert method.field_headers[0].disambiguated == 'object_' # test w/o equal sign rule = http_pb2.HttpRule(**{v: '/v1/{parent}/topics'}) method = make_method('DoSomething', http_rule=rule) assert method.field_headers == (wrappers.FieldHeader('parent'), ) assert method.field_headers[0].raw == 'parent' assert method.field_headers[0].disambiguated == 'parent'
def test_operation_polling_method(): T = descriptor_pb2.FieldDescriptorProto.Type operation = make_message( name="Operation", fields=(make_field( name=name, type=T.Value("TYPE_STRING"), number=i) for i, name in enumerate( ("name", "status", "error_code", "error_message"), start=1)), ) for f in operation.field: options = descriptor_pb2.FieldOptions() # Note: The field numbers were carefully chosen to be the corresponding enum values. options.Extensions[ex_ops_pb2.operation_field] = f.number f.options.MergeFrom(options) request = make_message( name="GetOperation", fields=[ make_field(name="name", type=T.Value("TYPE_STRING"), number=1) ], ) options = descriptor_pb2.MethodOptions() options.Extensions[ex_ops_pb2.operation_polling_method] = True polling_method = make_method( name="Get", input_message=request, output_message=operation, options=options, ) # Even though polling_method returns an Operation, it isn't an LRO ops_service = make_service( name="CustomOperations", methods=[ polling_method, make_method( name="Delete", input_message=make_message(name="Input"), output_message=make_message("Output"), ), ], ) assert ops_service.custom_polling_method == polling_method # Methods are LROs, so they are not polling methods user_service = make_service( name="ComputationStarter", methods=[ make_method( name="Start", input_message=make_message(name="StartRequest"), output_message=operation, ), ], ) assert not user_service.custom_polling_method
def test_method_http_options_empty_http_rule(): http_rule = http_pb2.HttpRule() method = make_method('DoSomething', http_rule=http_rule) assert method.http_options == [] http_rule = http_pb2.HttpRule(get='') method = make_method('DoSomething', http_rule=http_rule) assert method.http_options == []
def test_method_path_params(): # tests only the basic case of grpc transcoding http_rule = http_pb2.HttpRule(post='/v1/{project}/topics') method = make_method('DoSomething', http_rule=http_rule) assert method.path_params == ['project'] http_rule2 = http_pb2.HttpRule(post='/v1beta1/{name=rooms/*/blurbs/*}') method2 = make_method("DoSomething", http_rule=http_rule2) assert method2.path_params == ["name"]
def test_is_operation_polling_method(): T = descriptor_pb2.FieldDescriptorProto.Type operation = make_message( name="Operation", fields=[ make_field(name=name, type=T.Value("TYPE_STRING"), number=i) for i, name in enumerate( ("name", "status", "error_code", "error_message"), start=1) ], ) for f in operation.fields.values(): options = descriptor_pb2.FieldOptions() # Note: The field numbers were carefully chosen to be the corresponding enum values. options.Extensions[ex_ops_pb2.operation_field] = f.number f.options.MergeFrom(options) request = make_message( name="GetOperation", fields=[ make_field(name="name", type=T.Value("TYPE_STRING"), number=1) ], ) # Correct positive options = descriptor_pb2.MethodOptions() options.Extensions[ex_ops_pb2.operation_polling_method] = True polling_method = make_method( name="Get", input_message=request, output_message=operation, options=options, ) assert polling_method.is_operation_polling_method # Normal method that returns operation normal_method = make_method( name="Get", input_message=request, output_message=operation, ) assert not normal_method.is_operation_polling_method # Method with invalid options combination response = make_message(name="Response", fields=[make_field(name="name")]) invalid_method = make_method( name="Get", input_message=request, output_message=response, options=options, # Reuse options from the actual polling method ) assert not invalid_method.is_operation_polling_method
def test_transport_safe_name(): unsafe_methods = { name: make_method(name=name) for name in ["CreateChannel", "GrpcChannel", "OperationsClient"] } safe_methods = { name: make_method(name=name) for name in ["Call", "Put", "Hold", "Raise"] } for name, method in safe_methods.items(): assert method.transport_safe_name == name for name, method in unsafe_methods.items(): assert method.transport_safe_name == f"{name}_"
def test_method_include_flattened_message_fields(): a = make_field('a', type=5) b = make_field('b', type=11, type_name='Eggs', message=make_message('Eggs')) input_msg = make_message('Z', fields=(a, b)) method = make_method('F', input_message=input_msg, signatures=('a,b',)) assert len(method.flattened_fields) == 2
def test_method_http_options_generate_sample_implicit_template(): http_rule = http_pb2.HttpRule( get='/v1/{resource.id}/stuff', ) method = make_method( 'DoSomething', make_message( name="Input", fields=[ make_field( name="resource", number=1, message=make_message( "Resource", fields=[ make_field(name="id", type=9), ], ), ), ], ), http_rule=http_rule, ) sample = method.http_options[0].sample_request(method) assert sample == {'resource': { 'id': 'sample1'}}
def test_method_http_options_additional_bindings(): http_rule = http_pb2.HttpRule( post='/v1/{parent=projects/*}/topics', body='*', additional_bindings=[ http_pb2.HttpRule( post='/v1/{parent=projects/*/regions/*}/topics', body='*', ), http_pb2.HttpRule( post='/v1/projects/p1/topics', body='body_field', ), ] ) method = make_method('DoSomething', http_rule=http_rule) assert [dataclasses.asdict(http) for http in method.http_options] == [ { 'method': 'post', 'uri': '/v1/{parent=projects/*}/topics', 'body': '*' }, { 'method': 'post', 'uri': '/v1/{parent=projects/*/regions/*}/topics', 'body': '*' }, { 'method': 'post', 'uri': '/v1/projects/p1/topics', 'body': 'body_field' }]
def test_method_http_options_generate_sample(): http_rule = http_pb2.HttpRule( get='/v1/{resource.id=projects/*/regions/*/id/**}/stuff', ) method = make_method( 'DoSomething', make_message( name="Input", fields=[ make_field( name="resource", number=1, type=11, message=make_message( "Resource", fields=[ make_field(name="id", type=9), ], ), ), ], ), http_rule=http_rule, ) sample = method.http_options[0].sample_request(method) assert sample == { 'resource': { 'id': 'projects/sample1/regions/sample2/id/sample3' } }
def test_has_pagers(): paged = make_field(name='foos', message=make_message('Foo'), repeated=True) input_msg = make_message( name='ListFoosRequest', fields=( make_field(name='parent', type=9), # str make_field(name='page_size', type=5), # int make_field(name='page_token', type=9), # str ), ) output_msg = make_message( name='ListFoosResponse', fields=( paged, make_field(name='next_page_token', type=9), # str ), ) method = make_method( 'ListFoos', input_message=input_msg, output_message=output_msg, ) service = make_service( name="Fooer", methods=(method, ), ) assert service.has_pagers other_service = make_service( name="Unfooer", methods=(get_method("Unfoo", "foo.bar.UnfooReq", "foo.bar.UnFooResp"), ), ) assert not other_service.has_pagers
def test_method_paged_result_ref_types(): input_msg = make_message( name='ListSquidsRequest', fields=( make_field(name='parent', type=9), # str make_field(name='page_size', type=5), # int make_field(name='page_token', type=9), # str ), module='squid', ) mollusc_msg = make_message('Mollusc', module='mollusc') output_msg = make_message(name='ListMolluscsResponse', fields=(make_field(name='molluscs', message=mollusc_msg, repeated=True), make_field(name='next_page_token', type=9)), module='mollusc') method = make_method('ListSquids', input_message=input_msg, output_message=output_msg, module='squid') ref_type_names = {t.name for t in method.ref_types} assert ref_type_names == { 'ListSquidsRequest', 'ListSquidsPager', 'ListSquidsAsyncPager', 'Mollusc', }
def test_safe_name(): unsafe_methods = { name: make_method(name=name) for name in ["import", "Import", "Raise"] } safe_methods = { name: make_method(name=name) for name in ["Call", "Put", "Hold"] } for name, method in safe_methods.items(): assert method.safe_name == name for name, method in unsafe_methods.items(): assert method.safe_name == f"{name}_"
def test_method_http_opt_no_body(): http_rule = http_pb2.HttpRule(post='/v1/{parent=projects/*}/topics') method = make_method('DoSomething', http_rule=http_rule) assert method.http_opt == { 'verb': 'post', 'url': '/v1/{parent=projects/*}/topics' }
def test_resource_response(): # Top level response resource squid_resource = make_message("Squid", options=make_resource_opts("squid")) squid_request = make_message("CreateSquidRequest") # Nested response resource clam_resource = make_message("Clam", options=make_resource_opts("clam")) clam_response = make_message( "CreateClamResponse", fields=(make_field('clam', message=clam_resource), ), ) clam_request = make_message("CreateClamRequest") mollusc_service = make_service( "MolluscService", methods=(make_method(f"{request.name}", request, response) for request, response in ( (squid_request, squid_resource), (clam_request, clam_response), )), ) expected = {squid_resource, clam_resource} actual = mollusc_service.resource_messages assert expected == actual
def test_method_flattened_fields_different_package_non_primitive(): # This test verifies that method flattening handles a special case where: # * the method's request message type lives in a different package and # * a field in the method_signature is a non-primitive. # # If the message is defined in a different package it is not guaranteed to # be a proto-plus wrapped type, which puts restrictions on assigning # directly to its fields, which complicates request construction. # The easiest solution in this case is to just prohibit these fields # in the method flattening. message = make_message('Mantle', package="mollusc.cephalopod.v1", module="squid") mantle = make_field('mantle', type=11, type_name='Mantle', message=message, meta=message.meta) arms_count = make_field('arms_count', type=5, meta=message.meta) input_message = make_message('Squid', fields=(mantle, arms_count), package=".".join( message.meta.address.package), module=message.meta.address.module) method = make_method('PutSquid', input_message=input_message, package="remote.package.v1", module="module", signatures=("mantle,arms_count", )) assert set(method.flattened_fields) == {'arms_count'}
def test_method_flattened_fields(): a = make_field('a', type=5) # int b = make_field('b', type=5) input_msg = make_message('Z', fields=(a, b)) method = make_method('F', input_message=input_msg, signatures=('a,b', )) assert len(method.flattened_fields) == 2 assert 'a' in method.flattened_fields assert 'b' in method.flattened_fields
def test_method_types(): input_msg = make_message(name='Input', module='baz') output_msg = make_message(name='Output', module='baz') method = make_method('DoSomething', input_msg, output_msg, package='foo.bar', module='bacon') assert method.name == 'DoSomething' assert method.input.name == 'Input' assert method.output.name == 'Output'
def test_body_fields_no_body(): http_rule = http_pb2.HttpRule( post='/v1/{arms_shape=arms/*}/squids', ) method = make_method( 'PutSquid', http_rule=http_rule) assert not method.body_fields
def test_resource_messages(): # Regular, top level resource squid_resource = make_message("Squid", options=make_resource_opts("squid")) squid_request = make_message( "CreateSquid", fields=(make_field('squid', message=squid_resource), ), ) # Nested resource squamosa_message = make_message( "Squamosa", options=make_resource_opts("clam", "squamosa"), ) clam_resource = make_message( "Clam", options=make_resource_opts("clam"), fields=(make_field('squamosa', message=squamosa_message), ), ) clam_request = make_message( 'CreateClam', fields=( make_field('clam', message=clam_resource), # Red herring, not resources :) make_field('zone', 2, enum=make_enum('Zone')), make_field('pearls', 3, True, message=make_message('Pearl')), ), ) # Some special APIs have request messages that _are_ resources. whelk_resource = make_message("Whelk", options=make_resource_opts("whelk")) # Not a resource octopus_request = make_message( "CreateOctopus", fields=(make_field('Octopus', message=make_message('Octopus')), ), ) service = make_service('Molluscs', methods=(make_method( f"{message.name}", input_message=message, ) for message in ( squid_request, clam_request, whelk_resource, octopus_request, ))) expected = { squid_resource, clam_resource, whelk_resource, squamosa_message, } actual = service.resource_messages assert expected == actual
def test_method_routing_rule(): routing_rule = routing_pb2.RoutingRule() param = routing_rule.routing_parameters.add() param.field = 'table_name' param.path_template = 'projects/*/{table_location=instances/*}/tables/*' method = make_method('DoSomething', routing_rule=routing_rule) assert method.explicit_routing assert method.routing_rule.routing_parameters == [wrappers.RoutingParameter( x.field, x.path_template) for x in routing_rule.routing_parameters] assert method.routing_rule.routing_parameters[0].sample_request is not None
def test_method_http_options_reserved_name_in_url(): http_rule = http_pb2.HttpRule(post='/v1/license/{license=lic/*}', body='*') method = make_method('DoSomething', http_rule=http_rule) assert [dataclasses.asdict(http) for http in method.http_options] == [{ 'method': 'post', 'uri': '/v1/license/{license_=lic/*}', 'body': '*' }]
def test_method_http_options_body_field(): http_rule = http_pb2.HttpRule( post='/v1/{parent=projects/*}/topics', body='body_field' ) method = make_method('DoSomething', http_rule=http_rule) assert [dataclasses.asdict(http) for http in method.http_options] == [{ 'method': 'post', 'uri': '/v1/{parent=projects/*}/topics', 'body': 'body_field' }]
def test_method_query_params(): # tests only the basic case of grpc transcoding http_rule = http_pb2.HttpRule(post='/v1/{project}/topics', body='address') input_message = make_message('MethodInput', fields=(make_field('region'), make_field('project'), make_field('address'))) method = make_method('DoSomething', http_rule=http_rule, input_message=input_message) assert method.query_params == {'region'}
def test_flattened_oneof_fields(): mass_kg = make_field(name="mass_kg", oneof="mass", type=5) mass_lbs = make_field(name="mass_lbs", oneof="mass", type=5) length_m = make_field(name="length_m", oneof="length", type=5) length_f = make_field(name="length_f", oneof="length", type=5) color = make_field(name="color", type=5) mantle = make_field( name="mantle", message=make_message( name="Mantle", fields=( make_field(name="color", type=5), mass_kg, mass_lbs, ), ), ) request = make_message( name="CreateMolluscReuqest", fields=( length_m, length_f, color, mantle, ), ) method = make_method( name="CreateMollusc", input_message=request, signatures=[ "length_m,", "length_f,", "mantle.mass_kg,", "mantle.mass_lbs,", "color", ] ) expected = {"mass": [mass_kg, mass_lbs], "length": [length_m, length_f]} actual = method.flattened_oneof_fields() assert expected == actual # Check this method too becasue the setup is a lot of work. expected = { "color": "color", "length_m": "length_m", "length_f": "length_f", "mass_kg": "mantle.mass_kg", "mass_lbs": "mantle.mass_lbs", } actual = method.flattened_field_to_key assert expected == actual
def test_service_any_deprecated(): service = make_service( name='Service', methods=((make_method( f"GetMollusc", input_message=make_message("GetMolluscRequest", ), output_message=make_message("GetMolluscResponse", ), ), ))) assert service.any_deprecated == False deprecated_service = make_service( name='ServiceWithDeprecatedMethod', methods=((make_method( f"GetMollusc", input_message=make_message("GetMolluscRequest", ), output_message=make_message("GetMolluscResponse", ), is_deprecated=True, ), ))) assert deprecated_service.any_deprecated == True
def test_method_client_output_paged(): paged = make_field(name='foos', message=make_message('Foo'), repeated=True) parent = make_field(name='parent', type=9) # str page_size = make_field(name='page_size', type=5) # int page_token = make_field(name='page_token', type=9) # str input_msg = make_message(name='ListFoosRequest', fields=( parent, page_size, page_token, )) output_msg = make_message( name='ListFoosResponse', fields=( paged, make_field(name='next_page_token', type=9), # str )) method = make_method( 'ListFoos', input_message=input_msg, output_message=output_msg, ) assert method.paged_result_field == paged assert method.client_output.ident.name == 'ListFoosPager' max_results = make_field(name='max_results', type=5) # int input_msg = make_message(name='ListFoosRequest', fields=( parent, max_results, page_token, )) method = make_method( 'ListFoos', input_message=input_msg, output_message=output_msg, ) assert method.paged_result_field == paged assert method.client_output.ident.name == 'ListFoosPager'
def test_method_http_options(): verbs = ['get', 'put', 'post', 'delete', 'patch'] for v in verbs: http_rule = http_pb2.HttpRule(**{v: '/v1/{parent=projects/*}/topics'}) method = make_method('DoSomething', http_rule=http_rule) assert [dataclasses.asdict(http) for http in method.http_options] == [{ 'method': v, 'uri': '/v1/{parent=projects/*}/topics', 'body': None }]
def test_method_field_headers_present(): verbs = [ 'get', 'put', 'post', 'delete', 'patch', ] for v in verbs: rule = http_pb2.HttpRule(**{v: '/v1/{parent=projects/*}/topics'}) method = make_method('DoSomething', http_rule=rule) assert method.field_headers == ('parent', )