gapic-generator 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -0
  3. data/lib/gapic/formatting_utils.rb +4 -2
  4. data/lib/gapic/generator/version.rb +1 -1
  5. data/lib/gapic/generators/default_generator.rb +11 -10
  6. data/lib/gapic/presenters/field_presenter.rb +1 -1
  7. data/lib/gapic/presenters/gem_presenter.rb +8 -2
  8. data/lib/gapic/presenters/method_presenter.rb +17 -2
  9. data/lib/gapic/presenters/package_presenter.rb +16 -1
  10. data/lib/gapic/presenters/resource_presenter.rb +3 -9
  11. data/lib/gapic/presenters/service_presenter.rb +57 -12
  12. data/lib/gapic/resource_lookup.rb +23 -45
  13. data/lib/gapic/ruby_info.rb +93 -0
  14. data/lib/gapic/schema/api.rb +102 -1
  15. data/lib/gapic/schema/loader.rb +9 -2
  16. data/lib/gapic/schema/wrappers.rb +109 -22
  17. data/templates/default/gem/entrypoint.erb +8 -0
  18. data/templates/default/gem/gemspec.erb +1 -1
  19. data/templates/default/gem/readme.erb +15 -1
  20. data/templates/default/gem/rubocop.erb +13 -41
  21. data/templates/default/lib/_package.erb +17 -0
  22. data/templates/default/lib/_service.erb +32 -0
  23. data/templates/default/package.erb +5 -5
  24. data/templates/default/service.erb +5 -7
  25. data/templates/default/service/_helpers.erb +3 -0
  26. data/templates/default/service/client.erb +1 -1
  27. data/templates/default/service/client/_client.erb +10 -16
  28. data/templates/default/service/client/_operations.erb +0 -4
  29. data/templates/default/service/client/_resource.erb +1 -1
  30. data/templates/default/service/client/method/docs/_request_normal.erb +10 -5
  31. data/templates/default/service/client/method/docs/_request_streaming.erb +1 -1
  32. data/templates/default/service/credentials.erb +1 -1
  33. data/templates/default/service/operations.erb +1 -1
  34. data/templates/default/service/paths.erb +1 -1
  35. data/templates/default/service/test/client.erb +16 -2
  36. data/templates/default/service/test/client_operations.erb +2 -2
  37. data/templates/default/service/test/method/_configure.erb +19 -0
  38. metadata +8 -3
  39. data/templates/default/service/client/_helpers.erb +0 -9
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2020 Google LLC
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Gapic
18
+ ##
19
+ # Various Ruby language information useful for generators.
20
+ #
21
+ module RubyInfo
22
+ class << self
23
+ ##
24
+ # A sorted list of Ruby's keywords.
25
+ #
26
+ # @see https://docs.ruby-lang.org/en/2.7.0/keywords_rdoc.html
27
+ #
28
+ # @return [Array<String>]
29
+ #
30
+ def keywords
31
+ @keywords ||= [
32
+ "__ENCODING__",
33
+ "__LINE__",
34
+ "__FILE__",
35
+ "BEGIN",
36
+ "END",
37
+ "alias",
38
+ "and",
39
+ "begin",
40
+ "break",
41
+ "case",
42
+ "class",
43
+ "def",
44
+ "defined?",
45
+ "do",
46
+ "else",
47
+ "elsif",
48
+ "end",
49
+ "ensure",
50
+ "false",
51
+ "for",
52
+ "if",
53
+ "in",
54
+ "module",
55
+ "next",
56
+ "nil",
57
+ "not",
58
+ "or",
59
+ "redo",
60
+ "rescue",
61
+ "retry",
62
+ "return",
63
+ "self",
64
+ "super",
65
+ "then",
66
+ "true",
67
+ "undef",
68
+ "unless",
69
+ "until",
70
+ "when",
71
+ "while",
72
+ "yield"
73
+ ].freeze
74
+ end
75
+
76
+ ##
77
+ # A sorted list of method names that generated code should avoid.
78
+ # This includes methods of the Object class (including BasicObject and
79
+ # Kernel), Ruby keywords, and a few special names including "initialize"
80
+ # and "configure".
81
+ #
82
+ # @return [Array<String>]
83
+ #
84
+ def excluded_method_names
85
+ @excluded_method_names ||= begin
86
+ object_methods = (Object.instance_methods + Object.private_instance_methods).map(&:to_s)
87
+ other_methods = ["configure", "initialize"]
88
+ (object_methods + other_methods + keywords).sort.freeze
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -51,6 +51,7 @@ module Gapic
51
51
  loader.load_file fd, request.file_to_generate.include?(fd.name)
52
52
  end
53
53
  @files.each { |f| f.parent = self }
54
+ @resource_types = analyze_resources
54
55
  end
55
56
 
56
57
  def containing_api
@@ -201,7 +202,7 @@ module Gapic
201
202
  config = config_file ? YAML.load_file(config_file) : {}
202
203
  protoc_options.each do |k, v|
203
204
  next if k == "configuration"
204
- branch = key_to_str(k).split(".").reverse.inject(v) { |m, s| { str_to_key(s) => m } }
205
+ branch = parse_key(key_to_str(k)).reverse.inject(v) { |m, s| { str_to_key(s) => m } }
205
206
  config = deep_merge config, branch
206
207
  end
207
208
  config
@@ -224,6 +225,21 @@ module Gapic
224
225
  configuration[:generate_path_helpers_output] ||= false
225
226
  end
226
227
 
228
+ # Whether the override_proto_namespaces parameter was given in the configuration
229
+ def override_proto_namespaces_defined?
230
+ configuration.key? :override_proto_namespaces
231
+ end
232
+
233
+ # Sets the override_proto_namespaces parameter in the configuration
234
+ def override_proto_namespaces= value
235
+ configuration[:override_proto_namespaces] = value
236
+ end
237
+
238
+ # Whether namespace overrides apply to proto/grpc class references
239
+ def override_proto_namespaces?
240
+ configuration[:override_proto_namespaces] ||= false
241
+ end
242
+
227
243
  # Raw parsed json of the combined grpc service config files if provided
228
244
  # or an empty hash if no config was provided
229
245
  def grpc_service_config_raw
@@ -243,8 +259,77 @@ module Gapic
243
259
  end
244
260
  end
245
261
 
262
+ # Get a resource given its type string
263
+ def lookup_resource_type resource_type
264
+ @resource_types[resource_type]
265
+ end
266
+
267
+ # Given a service, find all common services that use it as a delegate.
268
+ def common_services_for delegate
269
+ @delegate_to_common ||= begin
270
+ (configuration[:common_services] || {}).each_with_object({}) do |(c, d), mapping|
271
+ (mapping[d] ||= []) << c
272
+ end
273
+ end
274
+ all_services = services
275
+ @delegate_to_common.fetch(delegate.address.join("."), []).map do |addr|
276
+ addr = addr.split "."
277
+ all_services.find { |s| s.address == addr }
278
+ end.compact.uniq
279
+ end
280
+
281
+ # Given a common service, return its delegate.
282
+ def delegate_service_for common
283
+ addr = (configuration[:common_services] || {})[common.address.join "."]
284
+ return nil unless addr
285
+ addr = addr.split "."
286
+ services.find { |s| s.address == addr }
287
+ end
288
+
246
289
  private
247
290
 
291
+ # Does a pre-analysis of all resources defined in the job. This has
292
+ # two effects:
293
+ # * Side effect: each resource has its parent_resources field set.
294
+ # * A mapping from resource type to resource wrapper is returned.
295
+ def analyze_resources
296
+ # In order to set parent_resources, we first populate a mapping from
297
+ # parsed pattern to resource mapping (in the patterns variable). This
298
+ # is done in one pass along with populating the resource type mapping.
299
+ # Then, we go through all resources again, get its expected parent
300
+ # patterns, and anything that shows up in the patterns mapping is taken
301
+ # to be a parent.
302
+ types = {}
303
+ patterns = {}
304
+ @files.each do |file|
305
+ file.resources.each { |resource| populate_resource_lookups resource, types, patterns }
306
+ file.messages.each { |message| populate_message_resource_lookups message, types, patterns }
307
+ end
308
+ types.each do |_type, resource|
309
+ parents = resource.parsed_parent_patterns
310
+ .map { |pat| patterns[pat] }
311
+ .compact.uniq
312
+ resource.parent_resources.replace parents
313
+ end
314
+ types
315
+ end
316
+
317
+ def populate_resource_lookups resource, types, patterns
318
+ types[resource.type] = resource
319
+ resource.parsed_patterns.each do |pat|
320
+ patterns[pat] = resource
321
+ end
322
+ end
323
+
324
+ def populate_message_resource_lookups message, types, patterns
325
+ populate_resource_lookups message.resource, types, patterns if message.resource
326
+ message.nested_messages.each do |nested|
327
+ populate_message_resource_lookups nested, types, patterns
328
+ end
329
+ end
330
+
331
+ # Parse a comma-delimited list of equals-delimited lists of strings, while
332
+ # mapping backslash-escaped commas and equal signs to literal characters.
248
333
  def parse_parameter str
249
334
  str.scan(/\\.|,|=|[^\\,=]+/)
250
335
  .each_with_object([[String.new]]) do |tok, arr|
@@ -261,6 +346,22 @@ module Gapic
261
346
  end
262
347
  end
263
348
 
349
+ # split the string on periods, but map backslash-escaped periods to
350
+ # literal periods.
351
+ def parse_key str
352
+ str.scan(/\.|\\.|[^\.\\]+/)
353
+ .each_with_object([String.new]) do |tok, arr|
354
+ if tok == "."
355
+ arr.append String.new
356
+ elsif tok.start_with? "\\"
357
+ arr.last << tok[1]
358
+ else
359
+ arr.last << tok
360
+ end
361
+ arr
362
+ end
363
+ end
364
+
264
365
  def str_to_key str
265
366
  str = str.to_s
266
367
  str.start_with?(":") ? str[1..-1].to_sym : str
@@ -64,9 +64,13 @@ module Gapic
64
64
  load_service registry, s, address, docs, [6, i]
65
65
  end
66
66
 
67
+ # Load top-level resources
68
+ resource_descriptors = file_descriptor.options[:".google.api.resource_definition"] if file_descriptor.options
69
+ resources = Array(resource_descriptors).map { |descriptor| Resource.new descriptor }
70
+
67
71
  # Construct and return the file.
68
72
  File.new file_descriptor, address, docs[path], messages, enums,
69
- services, file_to_generate, registry
73
+ services, resources, file_to_generate, registry
70
74
  end
71
75
 
72
76
  # Updates the fields of a message and it's nested messages.
@@ -158,9 +162,12 @@ module Gapic
158
162
  load_field registry, e, address, docs, path + [6, i]
159
163
  end
160
164
 
165
+ resource_descriptor = descriptor.options[:".google.api.resource"] if descriptor.options
166
+ resource = resource_descriptor ? Resource.new(resource_descriptor) : nil
167
+
161
168
  # Construct, cache, and return.
162
169
  msg = Message.new(descriptor, address, docs[path], fields, extensions,
163
- nested_messages, nested_enums)
170
+ resource, nested_messages, nested_enums)
164
171
  @prior_messages << msg
165
172
  add_to_registry registry, address, msg
166
173
  end
@@ -357,16 +357,19 @@ module Gapic
357
357
  # Wrapper for a protobuf file.
358
358
  #
359
359
  # @!attribute [r] messages
360
- # @ return [Enumerable<Message>] The top level messages contained in
360
+ # @return [Enumerable<Message>] The top level messages contained in
361
361
  # this file.
362
362
  # @!attribute [r] enums
363
- # @ return [Enumerable<Enum>] The top level enums contained in this
363
+ # @return [Enumerable<Enum>] The top level enums contained in this
364
364
  # file.
365
365
  # @!attribute [r] services
366
- # @ return [Enumerable<Service>] The services contained in this file.
366
+ # @return [Enumerable<Service>] The services contained in this file.
367
+ # @!attribute [r] resources
368
+ # @return [Enumerable<Resource>] The top level resources contained in
369
+ # this file.
367
370
  class File < Proto
368
371
  extend Forwardable
369
- attr_reader :messages, :enums, :services, :registry
372
+ attr_reader :messages, :enums, :services, :resources, :registry
370
373
 
371
374
  # Initializes a message object.
372
375
  # @param descriptor [Google::Protobuf::DescriptorProto] the protobuf
@@ -379,20 +382,23 @@ module Gapic
379
382
  # file.
380
383
  # @param enums [Enumerable<Enum>] The top level enums of this file.
381
384
  # @param services [Enumerable<Service>] The services of this file.
385
+ # @param resources [Enumerable<Resource>] The resources from this file.
382
386
  # @param generate [Boolean] Whether this file should be generated.
383
387
  def initialize descriptor, address, docs, messages, enums, services,
384
- generate, registry
388
+ resources, generate, registry
385
389
  super descriptor, address, docs
386
390
  @messages = messages || []
387
391
  @enums = enums || []
388
392
  @services = services || []
393
+ @resources = resources || []
389
394
  @generate = generate
390
395
  @registry = registry
391
396
 
392
397
  # Apply parent
393
398
  @messages.each { |m| m.parent = self }
394
- @enums.each { |m| m.parent = self }
399
+ @enums.each { |m| m.parent = self }
395
400
  @services.each { |m| m.parent = self }
401
+ @resources.each { |m| m.parent = self }
396
402
  end
397
403
 
398
404
  def containing_file
@@ -413,13 +419,6 @@ module Gapic
413
419
  options[:ruby_package] if options
414
420
  end
415
421
 
416
- # @return [Array<Google::Api::ResourceDescriptor>] A representation of the resource.
417
- # This is generally intended to be attached to the "name" field.
418
- # See `google/api/resource.proto`.
419
- def resources
420
- options[:".google.api.resource_definition"] if options
421
- end
422
-
423
422
  # @!method name
424
423
  # @return [String] file name, relative to root of source tree.
425
424
  # @!method package
@@ -511,6 +510,8 @@ module Gapic
511
510
  # @ return [Enumerable<Field>] The fields of a message.
512
511
  # @!attribute [r] extensions
513
512
  # @ return [Enumerable<Field>] The extensions of a message.
513
+ # @!attribute [r] resource
514
+ # @ return [Resource,nil] A representation of the resource.
514
515
  # @!attribute [r] nested_messages
515
516
  # @ return [Enumerable<Message>] The nested message declarations of a
516
517
  # message.
@@ -518,7 +519,7 @@ module Gapic
518
519
  # @ return [Enumerable<Enum>] The nested enum declarations of a message.
519
520
  class Message < Proto
520
521
  extend Forwardable
521
- attr_reader :fields, :extensions, :nested_messages, :nested_enums
522
+ attr_reader :fields, :extensions, :resource, :nested_messages, :nested_enums
522
523
 
523
524
  # Initializes a message object.
524
525
  # @param descriptor [Google::Protobuf::DescriptorProto] the protobuf
@@ -529,15 +530,17 @@ module Gapic
529
530
  # of the proto. See #docs for more info.
530
531
  # @param fields [Enumerable<Field>] The fields of this message.
531
532
  # @param extensions [Enumerable<Field>] The extensions of this message.
533
+ # @param resource [Resource,nil] The resource of this message, or nil if none.
532
534
  # @param nested_messages [Enumerable<Message>] The nested message
533
535
  # declarations of this message.
534
536
  # @param nested_enums [Enumerable<Enum>] The nested enum declarations
535
537
  # of this message.
536
- def initialize descriptor, address, docs, fields, extensions,
538
+ def initialize descriptor, address, docs, fields, extensions, resource,
537
539
  nested_messages, nested_enums
538
540
  super descriptor, address, docs
539
541
  @fields = fields || []
540
542
  @extensions = extensions || []
543
+ @resource = resource
541
544
  @nested_messages = nested_messages || []
542
545
  @nested_enums = nested_enums || []
543
546
 
@@ -545,13 +548,7 @@ module Gapic
545
548
  @extensions.each { |x| x.parent = self }
546
549
  @nested_messages.each { |m| m.parent = self }
547
550
  @nested_enums.each { |e| e.parent = self }
548
- end
549
-
550
- # @return [Google::Api::ResourceDescriptor] A representation of the resource.
551
- # This is generally intended to be attached to the "name" field.
552
- # See `google/api/resource.proto`.
553
- def resource
554
- options[:".google.api.resource"] if options
551
+ @resource.parent = self if @resource
555
552
  end
556
553
 
557
554
  # @return [Boolean] whether this type is a map entry
@@ -736,5 +733,95 @@ module Gapic
736
733
  :options
737
734
  )
738
735
  end
736
+
737
+ # Wrapper for a protobuf Resource.
738
+ #
739
+ # Unlike most wrappers, this does not subclass the {Proto} wrapper because
740
+ # it does not use the fields exposed by that wrapper (`address`, `docs`,
741
+ # etc.) This is here principally to augment the resource definition with
742
+ # information about resource parent-child relationships.
743
+ #
744
+ # Resource parentage is defined implicitly by path patterns. The algorithm
745
+ # is as follows:
746
+ # * If the final segment of a pattern is an ID segment (i.e. `*` or some
747
+ # `{name}`) then remove it and the previous segment (which we assume to
748
+ # be the corresponding collection identifier, as described in AIP-122.)
749
+ # The resulting pattern is what we expect a parent to have.
750
+ # * If the final segment is static, then assume the pattern represents a
751
+ # singleton resource (AIP-156) and remove only that one segment. The
752
+ # resulting pattern is what we expect a parent to have.
753
+ #
754
+ # The {Resource#parsed_parent_patterns} method returns the set of patterns
755
+ # we expect of parents. It is then possible to search for resources with
756
+ # those patterns to determine what the parents are.
757
+ #
758
+ # @!attribute [rw] parent
759
+ # @return [Gapic::Schema::File,Gapic::Schema::Message] The parent object.
760
+ # @!attribute [r] descriptor
761
+ # @return [Array<Gapic::Schema::ResourceDescriptor>] The resource
762
+ # descriptor.
763
+ # @!attribute [r] parsed_patterns
764
+ # @return [Array<Array<String>>] The normalized, segmented forms of the
765
+ # patterns. Normalized means all ID segments are replaced by asterisks
766
+ # to remove non-structural differences due to different names being
767
+ # used. Segmented means simply split on slashes.
768
+ # For example, if a pattern is `"projects/{project}""`, the
769
+ # corresponding parsed pattern would be `["projects", "*"]`.
770
+ # @!attribute [r] parent_resources
771
+ # @return [Array<Gapic::Schema::Resource>] Parent resources
772
+ class Resource
773
+ extend Forwardable
774
+ attr_reader :descriptor, :parsed_patterns, :parent_resources
775
+ attr_accessor :parent
776
+
777
+ # Initializes a resource object.
778
+ # @param descriptor [Google::Protobuf::ResourceDescriptor] the protobuf
779
+ # representation of this resource.
780
+ def initialize descriptor
781
+ @parent = nil
782
+ @descriptor = descriptor
783
+ @parsed_patterns = descriptor.pattern.map do |pattern|
784
+ pattern.split("/").map do |segment|
785
+ segment =~ %r{\{[^/\}]+(=[^\}]+)?\}} ? "*" : segment
786
+ end.freeze
787
+ end.freeze
788
+ @parent_resources = []
789
+ end
790
+
791
+ # Returns the "root" of this schema.
792
+ # @return [Gapic::Schema::Api]
793
+ def containing_api
794
+ parent&.containing_api
795
+ end
796
+
797
+ # Returns the file containing this proto entity
798
+ # @return [Gapic::Schema::File]
799
+ def containing_file
800
+ parent&.containing_file
801
+ end
802
+
803
+ # Returns parsed patterns for the expected parents.
804
+ # @return [Array<Array<String>>]
805
+ def parsed_parent_patterns
806
+ @parsed_patterns.map do |pat|
807
+ parent = pat.last =~ /^\*\*?$/ ? pat[0...-2] : pat[0...-1]
808
+ parent.empty? ? nil : parent
809
+ end.compact.uniq
810
+ end
811
+
812
+ # @!method type
813
+ # @return [String] the resource type string.
814
+ # @!method pattern
815
+ # @return [Array<String>] the set of patterns.
816
+ # @!method name_field
817
+ # @return [String] the field on the resource that designates the
818
+ # resource name field. If omitted, this is assumed to be "name".
819
+ def_delegators(
820
+ :descriptor,
821
+ :type,
822
+ :pattern,
823
+ :name_field
824
+ )
825
+ end
739
826
  end
740
827
  end