Skip to content

Commit

Permalink
fix: methods returning Operation w/o operation_info are now allowed. (#…
Browse files Browse the repository at this point in the history
…1047)

Formerly, a method that returned google.longrunning.Operation but did not have the google.longrunning.operation_info extension was considered an error; but there are valid cases for this, particularly in the Operation service itself.
This change makes it acceptable to have a method like this. It is not treated as a long-running operation.

Co-authored-by: Kenneth Bandes <kbandes@google.com>
  • Loading branch information
kbandes and Kenneth Bandes authored Oct 28, 2021
1 parent 3219085 commit 6b640af
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 2 deletions.
4 changes: 4 additions & 0 deletions gapic/schema/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,10 @@ def _maybe_get_lro(
# If the output type is google.longrunning.Operation, we use
# a specialized object in its place.
if meth_pb.output_type.endswith('google.longrunning.Operation'):
if not meth_pb.options.HasExtension(operations_pb2.operation_info):
# This is not a long running operation even though it returns
# an Operation.
return None
op = meth_pb.options.Extensions[operations_pb2.operation_info]
if not op.response_type or not op.metadata_type:
raise TypeError(
Expand Down
54 changes: 52 additions & 2 deletions tests/unit/schema/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -944,19 +944,69 @@ def test_lro():
assert len(lro_proto.messages) == 1


def test_lro_missing_annotation():
def test_lro_operation_no_annotation():
# A method that returns google.longrunning.Operation,
# but has no operation_info option, is treated as not lro.

# Set up a prior proto that mimics google/protobuf/empty.proto
lro_proto = api.Proto.build(make_file_pb2(
name='operations.proto', package='google.longrunning',
messages=(make_message_pb2(name='Operation'),),
), file_to_generate=False, naming=make_naming())

# Set up a method with an LRO but no annotation.
# Set up a method that returns an Operation, but has no annotation.
method_pb2 = descriptor_pb2.MethodDescriptorProto(
name='GetOperation',
input_type='google.example.v3.GetOperationRequest',
output_type='google.longrunning.Operation',
)

# Set up the service with an RPC.
service_pb = descriptor_pb2.ServiceDescriptorProto(
name='OperationService',
method=(method_pb2,),
)

# Set up the messages, including the annotated ones.
messages = (
make_message_pb2(name='GetOperationRequest', fields=()),
)

# Finally, set up the file that encompasses these.
fdp = make_file_pb2(
package='google.example.v3',
messages=messages,
services=(service_pb,),
)

# Make the proto object.
proto = api.Proto.build(fdp, file_to_generate=True, prior_protos={
'google/longrunning/operations.proto': lro_proto,
}, naming=make_naming())

service = proto.services['google.example.v3.OperationService']
method = service.methods['GetOperation']
assert method.lro is None


def test_lro_bad_annotation():
# Set up a prior proto that mimics google/protobuf/empty.proto
lro_proto = api.Proto.build(make_file_pb2(
name='operations.proto', package='google.longrunning',
messages=(make_message_pb2(name='Operation'),),
), file_to_generate=False, naming=make_naming())

# Set up a method with an LRO and incomplete annotation.
method_pb2 = descriptor_pb2.MethodDescriptorProto(
name='AsyncDoThing',
input_type='google.example.v3.AsyncDoThingRequest',
output_type='google.longrunning.Operation',
)
method_pb2.options.Extensions[operations_pb2.operation_info].MergeFrom(
operations_pb2.OperationInfo(
response_type='google.example.v3.AsyncDoThingResponse',
),
)

# Set up the service with an RPC.
service_pb = descriptor_pb2.ServiceDescriptorProto(
Expand Down

0 comments on commit 6b640af

Please sign in to comment.