Skip to content

Commit

Permalink
fix: resource messages in method response types generate helpers (#629)
Browse files Browse the repository at this point in the history
Some resource messages are only referenced in method responses, either
directly (the method returns a resource) or indirectly (the resource
is a field for some other message).
These response-resources now generate helper methods in client
classes.

Contains a minor formatting fix in the generated output, a minor fix
in an error message, and a tweak to nox.py to aid interactive debugging.
  • Loading branch information
software-dov authored Sep 30, 2020
1 parent 57c0423 commit 52bfd6d
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 19 deletions.
2 changes: 1 addition & 1 deletion gapic/schema/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ def __init__(
else:
raise TypeError(
f"Unknown type referenced in "
"{self.file_descriptor.name}: '{key}'"
f"{self.file_descriptor.name}: '{key}'"
)

# Only generate the service if this is a target file to be generated.
Expand Down
11 changes: 8 additions & 3 deletions gapic/schema/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,7 @@ def names(self) -> FrozenSet[str]:
@utils.cached_property
def resource_messages(self) -> FrozenSet[MessageType]:
"""Returns all the resource message types used in all
request fields in the service."""
request and response fields in the service."""
def gen_resources(message):
if message.resource_path:
yield message
Expand All @@ -1022,9 +1022,14 @@ def gen_resources(message):
yield type_

return frozenset(
resource_msg
msg
for method in self.methods.values()
for resource_msg in gen_resources(method.input)
for msg in chain(
gen_resources(method.input),
gen_resources(
method.lro.response_type if method.lro else method.output
),
)
)

@utils.cached_property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
"""Parse a {{ message.resource_type|snake_case }} path into its component segments."""
m = re.match(r"{{ message.path_regex_str }}", path)
return m.groupdict() if m else {}

{% endfor %} {# resources #}
{% for resource_msg in service.common_resources|sort(attribute="type_name") -%}
@staticmethod
Expand Down
19 changes: 12 additions & 7 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,18 @@ def unit(session):

session.run(
"py.test",
"-vv",
"-n=auto",
"--cov=gapic",
"--cov-config=.coveragerc",
"--cov-report=term",
"--cov-report=html",
*(session.posargs or [path.join("tests", "unit")]),
*(
session.posargs
or [
"-vv",
"-n=auto",
"--cov=gapic",
"--cov-config=.coveragerc",
"--cov-report=term",
"--cov-report=html",
path.join("tests", "unit"),
]
),
)


Expand Down
57 changes: 49 additions & 8 deletions tests/unit/schema/wrappers/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,24 @@
make_service_with_method_options,
)

################################
# Helper Functions
################################


def make_resource_opts(*args):
# Resources are labeled via an options extension
opts = descriptor_pb2.MessageOptions()
opts.Extensions[resource_pb2.resource].pattern.append(
"/".join("{{{arg}}}/{arg}" for arg in args)
)
return opts


################################
# End Helper Functions
################################


def test_service_properties():
service = make_service(name='ThingDoer')
Expand Down Expand Up @@ -162,14 +180,6 @@ def test_module_name():


def test_resource_messages():
# Resources are labeled via an options extension
def make_resource_opts(*args):
opts = descriptor_pb2.MessageOptions()
opts.Extensions[resource_pb2.resource].pattern.append(
"/".join("{{{arg}}}/{arg}" for arg in args)
)
return opts

# Regular, top level resource
squid_resource = make_message("Squid", options=make_resource_opts("squid"))
squid_request = make_message(
Expand Down Expand Up @@ -336,3 +346,34 @@ def test_common_resource_patterns():
assert species_msg.resource_type == "Species"
assert species_msg.resource_path_args == ["family", "genus", "species"]
assert species_msg.path_regex_str == '^families/(?P<family>.+?)/genera/(?P<genus>.+?)/species/(?P<species>.+?)$'


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

0 comments on commit 52bfd6d

Please sign in to comment.