-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: HttpJson tests generate request for Mixins with custom paths #2840
base: main
Are you sure you want to change the base?
Conversation
...src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java
Outdated
Show resolved
Hide resolved
} | ||
} | ||
// Unable to match the possible resources with the HttpBindings | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the behavior if we couldn't find a matching resource with the HttpBindings? Is the generated tests going to pass?
I feel we should throw exception as this implies something is misconfigured. IIRC, this is one of the common reason that we receive YAQS questions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will create a default value with the createValue() method. The tests will pass since the default value will just be a value that matches the HttpBindings.
i.e. The tests will go from something like
setResource(ProjectName.of("[Project]"]))
to
setResource("projects/project-134")
where the resource value is just the string version of the resource.
I would like to throw an exception here, but I think this would end up breaking a bunch of existing services that have some misconfigured protos. I think we may need to ask the service teams to fix their protos before I start to throwing an exception here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would end up breaking a bunch of existing services that have some misconfigured protos.
I see, I don't think we should break the generation for those services but I think those generated tests should never worked?
In your example, assuming we have a HttpBinding misconfigured, and the test is generated to the following
setResource("projects/project-134/foo")
Is the test going to pass?
With this misconfiguration, the test would fail previously because we would generate setResource
based on the resource pattern only, hence ProjectName.of("[Project]"])
would not match projects/project-134/foo
on running the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this misconfiguration, the test would fail previously because we would generate setResource based on the resource pattern only
Currently, for this bindings not matching the resource pattern misconfiguration, that's true. It would fail the test and we would see it in the CI.
However, if we throw an exception on this case now, I think we may break for very niche cases. i.e. ServiceManagement comes to mind. It does not have any resources defined inside the library, but uses IAM mixins. The IAM mixin RPCs have resources defined in the httpbindings and the request messages have fields with resource_reference. It's not an RPC that the service team wrote, but is using. This would require them to add a resource for the service/*
pattern (to be clear, I think it's probably something they should do, but currently don't).
boolean isFieldUsedInPath = | ||
bindings != null && bindings.getPathParametersValuePatterns().containsKey(field.name()); | ||
if (isFieldUsedInPath) { | ||
for (ResourceName resourceName : matchedResources) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Rename resourceName
to matchedResourceName
so that we can change x
to resourceName
below. Maybe we can change the parameter resourceNames
to allResourceNames
as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. I'm also thinking about changing this method to return all matched resource names
...src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java
Outdated
Show resolved
Hide resolved
for (ResourceName resourceName : matchedResources) { | ||
// If the resource is matched as a wildcard `*`, the field's resource_reference is a | ||
// wildcard. Attempt to match with a known resource. | ||
if (resourceName.isOnlyWildcard()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exactly does isOnlyWildcard
mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe If the resource's pattern is *
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Do you have an example of it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Asset resource is defined as wildcard pattern: https://github.com/googleapis/googleapis/blob/573205a81b9b96ea6d4e0365373839b59f4b427e/google/cloud/asset/v1/assets.proto#L96-L99
so it could be something with this kind of resource reference: https://github.com/googleapis/googleapis/blob/573205a81b9b96ea6d4e0365373839b59f4b427e/google/cloud/asset/v1/asset_service.proto#L451-L456
The resource_reference for a field can be wildcard: https://github.com/googleapis/googleapis/blob/573205a81b9b96ea6d4e0365373839b59f4b427e/google/iam/v1/iam_policy.proto#L104-L106
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we create a golden test with a test service yaml that mimics the exact scenario(overrides mixin path)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. I will attempt to re-create this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a resource_name_testing.proto to generate a bunch of common test scenarios. I can't find an easy way to incorporate IAM mixins as the logic for golden files parses an individual service and not multiple services (i.e. Echo + IAM) to combine the RPCs.
I modelled the ServiceManagement situation in the WildcardResourceReference
and WildcardResourceReferenceStrictBindings
RPCs
List<ResourceName> parentResources = | ||
findParentResources(matchedResources.get(0), new ArrayList<>(resourceNames.values())); | ||
if (!parentResources.isEmpty()) { | ||
matchedResources = parentResources; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic here is OK, but I feel we may want to differentiate the concept of matchedResources
and derivedResources
/flattenedResources
. The matched resource is actually always one, but we could derive more resources from it if it is of child type. Hence putting the matched resource to a list and reusing/reassigning the same list is a little confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Let me rename this from matchedResources into something less confusing since I do think I'm using the term match
to mean multiple things.
For fields with direct resource_references, matched resources is always one (assuming that the resource exists and is known). However, it is possible that the pattern doesn't match to the bindings so it really isn't "matched" yet.
For child_type fields, we must try to use the parent resources. However, we still need to match the parent resources with the bindings, so they're technically not matched yet either.
d6eae01
to
9a1b1c7
Compare
Generated output for Java files via Hermetic Build: googleapis/google-cloud-java#10952 |
@@ -249,11 +251,11 @@ private static Optional<ResourceName> findParentResource( | |||
for (String childPattern : childResource.patterns()) { | |||
Optional<String> parentPattern = ResourceReferenceUtils.parseParentPattern(childPattern); | |||
if (parentPattern.isPresent() && patternToResourceName.containsKey(parentPattern.get())) { | |||
return Optional.of(patternToResourceName.get(parentPattern.get())); | |||
parentResources.add(patternToResourceName.get(parentPattern.get())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since at most one parent resources is used, why not stop adding to the list once we find a resource?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is possible that the first parent resource found does not match the bindings.
For example:
Resource has these patterns:
parentResources/{parentResource}/resources/{resource}
projects/{project}/locations/{location}
examples/{example}/testing/{test}
This would have these parent patterns:
parentResources/{parentResource}
projects/{project}
examples/{example}
If an RPC has these bindings:
parent={parentResources/*}
parent={projects/*}
It may be possible that the first matched parent resource is examples/*
, which does not match the bindings.
...src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java
Outdated
Show resolved
Hide resolved
...src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java
Show resolved
Hide resolved
...src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java
Show resolved
Hide resolved
…pic/composer/defaultvalue/DefaultValueComposer.java Co-authored-by: Joe Wang <[email protected]>
Quality Gate passed for 'gapic-generator-java-root'Issues Measures |
Quality Gate passed for 'java_showcase_integration_tests'Issues Measures |
Fixes: #1839
Changes
When trying to generate default values for a message, the logic is updated for resource_references.
resource_reference.type
, the generator will try to match resource's pattern with the method's HttpBindings. If there are no bindings, then the configured resource reference is returned. If there are bindings and the resource's pattern does not match, then the resource_reference is not used. If everything looks fine, return the matched resource.resource_reference.child_type
, the generator will search for the resource's parent type(s). It will run the above logic on all the parent resources.If there is no resource matched, then the fallback will be to create a default value using
createValue()
.Fix for ServiceManagement Tests
Output of the generated tests:
This was done after re-generating the library locally and running
./gradlew clean build
.Fix for NetworkManagment Tests
Output of the generated tests:
This was done after re-generating the library locally and running
./gradlew clean build
.Golden Files Changes
*
). The generator will attempt to use any other known resource to populate, but the IAMPolicy protos have no known resources. Since there are no other resources it can use, the generator will not use a resource reference.The resource configuration for
logging.googleapis.com/Settings
:does not match the path in
GetSettings
:This results in the name being the value from
createValue()
.