Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion gapic/samplegen/samplegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class TransformedRequest:

# Resource patterns look something like
# kingdom/{kingdom}/phylum/{phylum}/class/{class}
RESOURCE_RE = re.compile(r"\{([^}/]+)\}")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This regex seemed to be serving the same purpose as the one in wrappers.py

RESOURCE_RE = wrappers.MessageType.PATH_ARG_RE

@classmethod
def build(
Expand Down Expand Up @@ -198,6 +198,11 @@ def build(
raise types.NoSuchResourcePattern(
f"Resource {resource_typestr} has no pattern with params: {attr_name_str}"
)
# This re-writes
# patterns like: 'projects/{project}/metricDescriptors/{metric_descriptor=**}'
# to 'projects/{project}/metricDescriptors/{metric_descriptor}
# so it can be used in sample code as an f-string.
pattern = cls.RESOURCE_RE.sub(r"{\g<1>}", pattern)

return cls(base=base, body=attrs, single=None, pattern=pattern,)

Expand Down
3 changes: 2 additions & 1 deletion gapic/schema/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,8 @@ def __getattr__(self, name):
class MessageType:
"""Description of a message (defined with the ``message`` keyword)."""
# Class attributes
PATH_ARG_RE = re.compile(r'\{([a-zA-Z0-9_-]+)\}')
# https://google.aip.dev/122
PATH_ARG_RE = re.compile(r'\{([a-zA-Z0-9_\-]+)(?:=\*\*)?\}')

# Instance attributes
message_pb: descriptor_pb2.DescriptorProto
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@ async def sample_list_resources():
part_id = "part_id_value"
parent = f"items/{item_id}/parts/{part_id}"

item_id = "item_id_value"
part_id = "part_id_value"
resource_with_wildcard = f"items/{item_id}/parts/{part_id}"

request = mollusca_v1.ListResourcesRequest(
parent=parent,
resource_with_wildcard=resource_with_wildcard,
)

# Make the request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@ def sample_list_resources():
part_id = "part_id_value"
parent = f"items/{item_id}/parts/{part_id}"

item_id = "item_id_value"
part_id = "part_id_value"
resource_with_wildcard = f"items/{item_id}/parts/{part_id}"

request = mollusca_v1.ListResourcesRequest(
parent=parent,
resource_with_wildcard=resource_with_wildcard,
)

# Make the request
Expand Down
19 changes: 16 additions & 3 deletions tests/snippetgen/snippets.proto
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,15 @@ message ListResourcesRequest {
(google.api.resource_reference) = {
type: "snippets.example.com/Resource"
}];

string resource_with_wildcard = 2 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {
type: "snippets.example.com/ResourceWithWildcardSegment"
}];

int32 page_size = 2;
string page_token = 3;
int32 page_size = 3;
string page_token = 4;
}

message ListResourcesResponse {
Expand All @@ -91,10 +97,17 @@ message Resource {
pattern: "items/{item_id}/parts/{part_id}"
};
string name = 1;
}


message ResourceWithWildcardSegment {
option (google.api.resource) = {
type: "snippets.example.com/ResourceWithWildcardSegment"
pattern: "items/{item_id}/parts/{part_id=**}"
};
string name = 1;
}


message MessageWithNesting {
message NestedMessage {
string required_string = 1 [(google.api.field_behavior) = REQUIRED];
Expand Down
21 changes: 21 additions & 0 deletions tests/unit/schema/wrappers/test_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,27 @@ def test_resource_path():
assert message.resource_type == "Class"


def test_resource_path_with_wildcard():
options = descriptor_pb2.MessageOptions()
resource = options.Extensions[resource_pb2.resource]
resource.pattern.append(
"kingdoms/{kingdom}/phyla/{phylum}/classes/{klass=**}")
resource.pattern.append(
"kingdoms/{kingdom}/divisions/{division}/classes/{klass}")
resource.type = "taxonomy.biology.com/Class"
message = make_message('Squid', options=options)

assert message.resource_path == "kingdoms/{kingdom}/phyla/{phylum}/classes/{klass=**}"
assert message.resource_path_args == ["kingdom", "phylum", "klass"]
assert message.resource_type == "Class"
assert re.match(message.path_regex_str,
"kingdoms/my-kingdom/phyla/my-phylum/classes/my-klass")
assert re.match(message.path_regex_str,
"kingdoms/my-kingdom/phyla/my-phylum/classes/my-klass/additional-segment")
assert re.match(message.path_regex_str,
"kingdoms/my-kingdom/phyla/my-phylum/classes/") is None


def test_parse_resource_path():
options = descriptor_pb2.MessageOptions()
resource = options.Extensions[resource_pb2.resource]
Expand Down