diff --git a/gapic-generator/lib/gapic/formatting_utils.rb b/gapic-generator/lib/gapic/formatting_utils.rb index 4afaf6eb7..b6627829b 100644 --- a/gapic-generator/lib/gapic/formatting_utils.rb +++ b/gapic-generator/lib/gapic/formatting_utils.rb @@ -116,7 +116,12 @@ def escape_line_braces line def format_line_xrefs api, line, disable_xrefs, transport while (m = @xref_detector.match line) entity = api.lookup m[:addr] - return line if entity.nil? + is_mixin_field_addr = Gapic::Model::Mixins.mixin_message_field_address?( + m[:addr], + gem_name: api.configuration.fetch(:gem, nil)&.fetch(:name, "") + ) + return line if entity.nil? || is_mixin_field_addr + text = m[:text] yard_link = disable_xrefs ? text : yard_link_for_entity(entity, text, transport) return line if yard_link.nil? diff --git a/gapic-generator/lib/gapic/model/mixins.rb b/gapic-generator/lib/gapic/model/mixins.rb index 1183b4833..e4062dd40 100644 --- a/gapic-generator/lib/gapic/model/mixins.rb +++ b/gapic-generator/lib/gapic/model/mixins.rb @@ -162,6 +162,25 @@ def self.mixin_service_address? service_address, gem_name: nil MIXIN_GEM_NAMES.include?(service_address) && gem_name != MIXIN_GEM_NAMES[service_address] end + ## + # Returns true if the given service address is a mixin. + # This just checks the service against a (hard-coded) set of known mixins. + # If `gem_name` is provided, objects from the package of the service + # that corresponds to that gem_name are not considered mixins. + # + # @param message_field_address [String,Array] The address (either array + # or dot-delimited) of the message or field to check. + # @param gem_name [String] The name of the gem. + # @return [boolean] + # + def self.mixin_message_field_address? message_field_address, gem_name: nil + message_field_address = message_field_address.join "." unless message_field_address.is_a? String + # NB: messages are checked against package, not service + service_address = MIXIN_GEM_NAMES.keys.find { |sn| message_field_address.start_with? MIXIN_PACKAGE_NAMES[sn] } + + !service_address.nil? && gem_name != MIXIN_GEM_NAMES[service_address] + end + private # @return [Enumerable] Names of all services that are specified @@ -179,6 +198,13 @@ def services_in_config }.freeze private_constant :MIXIN_GEM_NAMES + MIXIN_PACKAGE_NAMES = { + LOCATIONS_SERVICE => "google.cloud.location", + IAM_SERVICE => "google.iam.v1", + LRO_SERVICE => "google.longrunning.operations" + }.freeze + private_constant :MIXIN_PACKAGE_NAMES + # Since mixins are scope-limited to a couple of services, it is easier to # have these in lookup tables than to construct a ServicePresenter diff --git a/gapic-generator/test/gapic/mixins/mixins_test.rb b/gapic-generator/test/gapic/mixins/mixins_test.rb index 04d3bda8d..e5dd6c6ad 100644 --- a/gapic-generator/test/gapic/mixins/mixins_test.rb +++ b/gapic-generator/test/gapic/mixins/mixins_test.rb @@ -45,6 +45,25 @@ def test_mixin_service_address_checker refute Gapic::Model::Mixins.mixin_service_address? "testing.mixins.ServiceWithLoc" end + def test_mixin_message_field_address_checker + # sic. no `s` at the end, this is a message named `Location`, not service named `Locations` + assert Gapic::Model::Mixins.mixin_message_field_address? "google.cloud.location.Location" + assert Gapic::Model::Mixins.mixin_message_field_address? "google.cloud.location.Location.metadata" + + assert Gapic::Model::Mixins.mixin_message_field_address? "google.cloud.location.Location", + gem_name: "google-cloud-something-else" + + refute Gapic::Model::Mixins.mixin_message_field_address? "google.cloud.location.Location", + gem_name: "google-cloud-location" + refute Gapic::Model::Mixins.mixin_message_field_address? "google.cloud.location.Location.metadata", + gem_name: "google-cloud-location" + + assert Gapic::Model::Mixins.mixin_message_field_address? ["google", "iam", "v1", "Policy"] + assert Gapic::Model::Mixins.mixin_message_field_address? ["google", "iam", "v1", "Policy", "bindings"] + + refute Gapic::Model::Mixins.mixin_message_field_address? "testing.mixins.Request" + end + # Test the `Garbage` library, which does NOT have mixins specified # in its service.yaml (or service.yaml at all) def test_garbage_mixins diff --git a/gapic-generator/test/test_helper.rb b/gapic-generator/test/test_helper.rb index 81ff14b05..6217ba59c 100644 --- a/gapic-generator/test/test_helper.rb +++ b/gapic-generator/test/test_helper.rb @@ -344,6 +344,10 @@ def fix_namespace name def fix_service_name name @service_mapping[name] || name end + + def configuration + {} + end end # A fake request builder diff --git a/shared/gem_defaults.rb b/shared/gem_defaults.rb index 96a3bbb75..71f8f39af 100644 --- a/shared/gem_defaults.rb +++ b/shared/gem_defaults.rb @@ -156,6 +156,10 @@ def gem_defaults "testing/mixins/mixins.proto", "testing/routing_headers/routing_headers.proto", "testing/nonstandard_lro_grpc/nonstandard_lro_grpc.proto", + # `locations.proto` is included here because it is often + # included in the real world libraries + # as part of `files_to_generate` due to protoc limitations + "google/cloud/location/locations.proto", ], grpc_service_config: [ "../shared/protos/testing/grpc_service_config/grpc_service_config.json", diff --git a/shared/input/testing_desc.bin b/shared/input/testing_desc.bin index 7512734b9..6e78832e0 100644 Binary files a/shared/input/testing_desc.bin and b/shared/input/testing_desc.bin differ diff --git a/shared/output/cloud/secretmanager_v1beta1/proto_docs/google/iam/v1/policy.rb b/shared/output/cloud/secretmanager_v1beta1/proto_docs/google/iam/v1/policy.rb index 93a1fc570..79fdbd1e2 100644 --- a/shared/output/cloud/secretmanager_v1beta1/proto_docs/google/iam/v1/policy.rb +++ b/shared/output/cloud/secretmanager_v1beta1/proto_docs/google/iam/v1/policy.rb @@ -313,7 +313,7 @@ class AuditConfig # Specifies the identities that do not cause logging for this type of # permission. # Follows the same format of - # {::Google::Iam::V1::Binding#members Binding.members}. + # [Binding.members][google.iam.v1.Binding.members]. class AuditLogConfig include ::Google::Protobuf::MessageExts extend ::Google::Protobuf::MessageExts::ClassMethods diff --git a/shared/output/gapic/templates/garbage/proto_docs/google/iam/v1/policy.rb b/shared/output/gapic/templates/garbage/proto_docs/google/iam/v1/policy.rb index 3d5ab976b..63857e129 100644 --- a/shared/output/gapic/templates/garbage/proto_docs/google/iam/v1/policy.rb +++ b/shared/output/gapic/templates/garbage/proto_docs/google/iam/v1/policy.rb @@ -321,7 +321,7 @@ class AuditConfig # Specifies the identities that do not cause logging for this type of # permission. # Follows the same format of - # {::Google::Iam::V1::Binding#members Binding.members}. + # [Binding.members][google.iam.v1.Binding.members]. class AuditLogConfig include ::Google::Protobuf::MessageExts extend ::Google::Protobuf::MessageExts::ClassMethods diff --git a/shared/output/gapic/templates/testing/lib/google/cloud/location/locations_pb.rb b/shared/output/gapic/templates/testing/lib/google/cloud/location/locations_pb.rb new file mode 100644 index 000000000..9707d60e6 --- /dev/null +++ b/shared/output/gapic/templates/testing/lib/google/cloud/location/locations_pb.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/cloud/location/locations.proto + +require 'google/protobuf' + +require 'google/api/annotations_pb' +require 'google/protobuf/any_pb' +require 'google/api/client_pb' + + +descriptor_data = "\n%google/cloud/location/locations.proto\x12\x15google.cloud.location\x1a\x1cgoogle/api/annotations.proto\x1a\x19google/protobuf/any.proto\x1a\x17google/api/client.proto\"[\n\x14ListLocationsRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x66ilter\x18\x02 \x01(\t\x12\x11\n\tpage_size\x18\x03 \x01(\x05\x12\x12\n\npage_token\x18\x04 \x01(\t\"d\n\x15ListLocationsResponse\x12\x32\n\tlocations\x18\x01 \x03(\x0b\x32\x1f.google.cloud.location.Location\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"\"\n\x12GetLocationRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\xd7\x01\n\x08Location\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0blocation_id\x18\x04 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x05 \x01(\t\x12;\n\x06labels\x18\x02 \x03(\x0b\x32+.google.cloud.location.Location.LabelsEntry\x12&\n\x08metadata\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x32\xa4\x03\n\tLocations\x12\xab\x01\n\rListLocations\x12+.google.cloud.location.ListLocationsRequest\x1a,.google.cloud.location.ListLocationsResponse\"?\x82\xd3\xe4\x93\x02\x39\x12\x14/v1/{name=locations}Z!\x12\x1f/v1/{name=projects/*}/locations\x12\x9e\x01\n\x0bGetLocation\x12).google.cloud.location.GetLocationRequest\x1a\x1f.google.cloud.location.Location\"C\x82\xd3\xe4\x93\x02=\x12\x16/v1/{name=locations/*}Z#\x12!/v1/{name=projects/*/locations/*}\x1aH\xca\x41\x14\x63loud.googleapis.com\xd2\x41.https://www.googleapis.com/auth/cloud-platformBo\n\x19\x63om.google.cloud.locationB\x0eLocationsProtoP\x01Z=google.golang.org/genproto/googleapis/cloud/location;location\xf8\x01\x01\x62\x06proto3" + +pool = Google::Protobuf::DescriptorPool.generated_pool + +begin + pool.add_serialized_file(descriptor_data) +rescue TypeError + # Compatibility code: will be removed in the next major version. + require 'google/protobuf/descriptor_pb' + parsed = Google::Protobuf::FileDescriptorProto.decode(descriptor_data) + parsed.clear_dependency + serialized = parsed.class.encode(parsed) + file = pool.add_serialized_file(serialized) + warn "Warning: Protobuf detected an import path issue while loading generated file #{__FILE__}" + imports = [ + ["google.protobuf.Any", "google/protobuf/any.proto"], + ] + imports.each do |type_name, expected_filename| + import_file = pool.lookup(type_name).file_descriptor + if import_file.name != expected_filename + warn "- #{file.name} imports #{expected_filename}, but that import was loaded as #{import_file.name}" + end + end + warn "Each proto file must use a consistent fully-qualified name." + warn "This will become an error in the next major version." +end + +module Google + module Cloud + module Location + ListLocationsRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.cloud.location.ListLocationsRequest").msgclass + ListLocationsResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.cloud.location.ListLocationsResponse").msgclass + GetLocationRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.cloud.location.GetLocationRequest").msgclass + Location = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.cloud.location.Location").msgclass + end + end +end diff --git a/shared/output/gapic/templates/testing/lib/google/cloud/location/locations_services_pb.rb b/shared/output/gapic/templates/testing/lib/google/cloud/location/locations_services_pb.rb new file mode 100644 index 000000000..11d978858 --- /dev/null +++ b/shared/output/gapic/templates/testing/lib/google/cloud/location/locations_services_pb.rb @@ -0,0 +1,47 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# Source: google/cloud/location/locations.proto for package 'google.cloud.location' +# Original file comments: +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'grpc' +require 'google/cloud/location/locations_pb' + +module Google + module Cloud + module Location + module Locations + # An abstract interface that provides location-related information for + # a service. Service-specific metadata is provided through the + # [Location.metadata][google.cloud.location.Location.metadata] field. + class Service + + include ::GRPC::GenericService + + self.marshal_class_method = :encode + self.unmarshal_class_method = :decode + self.service_name = 'google.cloud.location.Locations' + + # Lists information about the supported locations for this service. + rpc :ListLocations, ::Google::Cloud::Location::ListLocationsRequest, ::Google::Cloud::Location::ListLocationsResponse + # Gets information about a location. + rpc :GetLocation, ::Google::Cloud::Location::GetLocationRequest, ::Google::Cloud::Location::Location + end + + Stub = Service.rpc_stub_class + end + end + end +end diff --git a/shared/output/gapic/templates/testing/lib/testing/mixins/mixins_pb.rb b/shared/output/gapic/templates/testing/lib/testing/mixins/mixins_pb.rb index 4a34f5574..ad1791cf2 100644 --- a/shared/output/gapic/templates/testing/lib/testing/mixins/mixins_pb.rb +++ b/shared/output/gapic/templates/testing/lib/testing/mixins/mixins_pb.rb @@ -10,7 +10,7 @@ require 'google/protobuf/empty_pb' -descriptor_data = "\n\x1btesting/mixins/mixins.proto\x12\x0etesting.mixins\x1a\x17google/api/client.proto\x1a\x1cgoogle/api/annotations.proto\x1a#google/longrunning/operations.proto\x1a\x1bgoogle/protobuf/empty.proto\"\t\n\x07Request\"\n\n\x08Response2\x97\x01\n\x0eServiceWithLoc\x12\x66\n\x06Method\x12\x17.testing.mixins.Request\x1a\x18.testing.mixins.Response\")\x82\xd3\xe4\x93\x02#\"\x1e/v1beta1/ServiceWithLoc/Method:\x01*\x1a\x1d\xca\x41\x1aservicewithloc.example.com2\xa4\x02\n\x1bServiceWithLocAndNonRestOps\x12\x66\n\x06Method\x12\x17.testing.mixins.Request\x1a\x18.testing.mixins.Response\")\x82\xd3\xe4\x93\x02#\"\x1e/v1beta1/ServiceWithLoc/Method:\x01*\x12x\n\tLROMethod\x12\x17.testing.mixins.Request\x1a\x1d.google.longrunning.Operation\"3\xca\x41\x30\n\x15google.protobuf.Empty\x12\x17testing.mixins.Response\x1a#\xca\x41 servicewithlocandops.example.comB\x12\xea\x02\x0fTesting::Mixinsb\x06proto3" +descriptor_data = "\n\x1btesting/mixins/mixins.proto\x12\x0etesting.mixins\x1a\x17google/api/client.proto\x1a\x1cgoogle/api/annotations.proto\x1a#google/longrunning/operations.proto\x1a\x1bgoogle/protobuf/empty.proto\"\t\n\x07Request\"\n\n\x08Response\"\x19\n\x17ReferencedInServiceYaml2\x97\x01\n\x0eServiceWithLoc\x12\x66\n\x06Method\x12\x17.testing.mixins.Request\x1a\x18.testing.mixins.Response\")\x82\xd3\xe4\x93\x02#\"\x1e/v1beta1/ServiceWithLoc/Method:\x01*\x1a\x1d\xca\x41\x1aservicewithloc.example.com2\xa4\x02\n\x1bServiceWithLocAndNonRestOps\x12\x66\n\x06Method\x12\x17.testing.mixins.Request\x1a\x18.testing.mixins.Response\")\x82\xd3\xe4\x93\x02#\"\x1e/v1beta1/ServiceWithLoc/Method:\x01*\x12x\n\tLROMethod\x12\x17.testing.mixins.Request\x1a\x1d.google.longrunning.Operation\"3\xca\x41\x30\n\x15google.protobuf.Empty\x12\x17testing.mixins.Response\x1a#\xca\x41 servicewithlocandops.example.comB\x12\xea\x02\x0fTesting::Mixinsb\x06proto3" pool = Google::Protobuf::DescriptorPool.generated_pool @@ -40,5 +40,6 @@ module Testing module Mixins Request = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("testing.mixins.Request").msgclass Response = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("testing.mixins.Response").msgclass + ReferencedInServiceYaml = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("testing.mixins.ReferencedInServiceYaml").msgclass end end diff --git a/shared/output/gapic/templates/testing/proto_docs/testing/mixins/mixins.rb b/shared/output/gapic/templates/testing/proto_docs/testing/mixins/mixins.rb index 07008920c..b48e4b672 100644 --- a/shared/output/gapic/templates/testing/proto_docs/testing/mixins/mixins.rb +++ b/shared/output/gapic/templates/testing/proto_docs/testing/mixins/mixins.rb @@ -36,5 +36,12 @@ class Response include ::Google::Protobuf::MessageExts extend ::Google::Protobuf::MessageExts::ClassMethods end + + # This comment tests a mixin documentation reference: + # [google.cloud.location.Location.metadata][google.cloud.location.Location.metadata]. + class ReferencedInServiceYaml + include ::Google::Protobuf::MessageExts + extend ::Google::Protobuf::MessageExts::ClassMethods + end end end diff --git a/shared/protos/testing/mixins/mixins.proto b/shared/protos/testing/mixins/mixins.proto index bdef1eb58..ee55ec027 100644 --- a/shared/protos/testing/mixins/mixins.proto +++ b/shared/protos/testing/mixins/mixins.proto @@ -43,3 +43,8 @@ message Request { message Response { } + +// This comment tests a mixin documentation reference: +// [google.cloud.location.Location.metadata][google.cloud.location.Location.metadata]. +message ReferencedInServiceYaml { +} diff --git a/shared/protos/testing/mixins/testing_service.yaml b/shared/protos/testing/mixins/testing_service.yaml index 609b6bd8a..32618db04 100644 --- a/shared/protos/testing/mixins/testing_service.yaml +++ b/shared/protos/testing/mixins/testing_service.yaml @@ -12,6 +12,9 @@ apis: - name: google.longrunning.Operations - name: google.cloud.location.Locations +types: +- name: testing.mixins.ReferencedInServiceYaml + http: rules: - selector: google.longrunning.Operations.ListOperations