gapic-generator 0.3.1 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/lib/gapic/formatting_utils.rb +9 -7
  4. data/lib/gapic/generator/version.rb +1 -1
  5. data/lib/gapic/generators/default_generator.rb +13 -11
  6. data/lib/gapic/helpers/filepath_helper.rb +1 -0
  7. data/lib/gapic/helpers/namespace_helper.rb +8 -1
  8. data/lib/gapic/path_template/segment.rb +7 -0
  9. data/lib/gapic/presenters/field_presenter.rb +9 -9
  10. data/lib/gapic/presenters/file_presenter.rb +1 -1
  11. data/lib/gapic/presenters/method_presenter.rb +23 -17
  12. data/lib/gapic/presenters/package_presenter.rb +8 -1
  13. data/lib/gapic/presenters/resource_presenter.rb +55 -35
  14. data/lib/gapic/presenters/service_presenter.rb +56 -19
  15. data/lib/gapic/ruby_info.rb +93 -0
  16. data/lib/gapic/schema/api.rb +63 -1
  17. data/templates/default/gem/gemfile.erb +3 -0
  18. data/templates/default/gem/gemspec.erb +3 -1
  19. data/templates/default/gem/rakefile.erb +1 -0
  20. data/templates/default/gem/test_helper.erb +8 -0
  21. data/templates/default/layouts/_ruby.erb +5 -4
  22. data/templates/default/proto_docs/_message.erb +2 -2
  23. data/templates/default/service/client.erb +1 -1
  24. data/templates/default/service/client/_client.erb +15 -4
  25. data/templates/default/service/client/_config.erb +32 -28
  26. data/templates/default/service/client/_credentials.erb +1 -1
  27. data/templates/default/service/client/_operations.erb +3 -1
  28. data/templates/default/service/client/method/def/_options_defaults.erb +2 -2
  29. data/templates/default/service/client/method/def/_request_normal.erb +2 -2
  30. data/templates/default/service/client/method/def/_request_streaming.erb +3 -3
  31. data/templates/default/service/client/method/def/_response_normal.erb +1 -1
  32. data/templates/default/service/client/method/def/_response_paged.erb +2 -2
  33. data/templates/default/service/client/method/docs/_error.erb +1 -1
  34. data/templates/default/service/client/method/docs/_request_normal.erb +2 -2
  35. data/templates/default/service/client/method/docs/_request_streaming.erb +2 -2
  36. data/templates/default/service/client/method/docs/_response.erb +1 -1
  37. data/templates/default/service/client/resource/_def.erb +1 -1
  38. data/templates/default/service/client/resource/_multi.erb +4 -7
  39. data/templates/default/service/client/resource/_single.erb +2 -3
  40. data/templates/default/service/credentials.erb +1 -1
  41. data/templates/default/service/operations.erb +1 -1
  42. data/templates/default/service/paths.erb +1 -1
  43. data/templates/default/service/test/_resource.erb +16 -0
  44. data/templates/default/service/test/client.erb +17 -4
  45. data/templates/default/service/test/client_operations.erb +3 -4
  46. data/templates/default/service/test/client_paths.erb +17 -0
  47. data/templates/default/service/test/method/_configure.erb +19 -0
  48. metadata +8 -3
@@ -55,8 +55,26 @@ module Gapic
55
55
  @service.address
56
56
  end
57
57
 
58
+ # Returns a presenter for this service's delegate (if it is a common service)
59
+ # otherwise returns `nil`.
60
+ def common_service_delegate
61
+ unless defined? @common_service_delegate
62
+ delegate = @api.delegate_service_for @service
63
+ @common_service_delegate = delegate ? ServicePresenter.new(@api, delegate) : nil
64
+ end
65
+ @common_service_delegate
66
+ end
67
+
68
+ # The namespace of the client. Normally this is the version module. This
69
+ # may be different from the proto namespace for a common service.
58
70
  def namespace
59
- return @service.ruby_package if @service.ruby_package.present?
71
+ # If this service is a common service, its client should go into its
72
+ # delegate's namespace rather than its own. For example, KMS includes
73
+ # the common IAMPolicy service, but that service's client should go
74
+ # into the KMS namespace.
75
+ return common_service_delegate.namespace if common_service_delegate
76
+
77
+ return ensure_absolute_namespace @service.ruby_package if @service.ruby_package.present?
60
78
 
61
79
  namespace = ruby_namespace_for_address @service.address[0...-1]
62
80
  fix_namespace @api, namespace
@@ -71,15 +89,25 @@ module Gapic
71
89
  end
72
90
 
73
91
  def name
74
- @service.name
92
+ @api.fix_service_name @service.name
93
+ end
94
+
95
+ # The namespace of the protos. This may be different from the client
96
+ # namespace for a common service.
97
+ def proto_namespace
98
+ return ensure_absolute_namespace @service.ruby_package if @service.ruby_package.present?
99
+
100
+ namespace = ruby_namespace_for_address @service.address[0...-1]
101
+ @api.override_proto_namespaces? ? fix_namespace(@api, namespace) : namespace
75
102
  end
76
103
 
77
104
  def proto_service_name_full
78
- fix_namespace @api, "#{namespace}::#{name}"
105
+ name_full = "#{proto_namespace}::#{@service.name}"
106
+ @api.override_proto_namespaces? ? fix_namespace(@api, name_full) : name_full
79
107
  end
80
108
 
81
109
  def module_name
82
- proto_service_name_full.split("::").last
110
+ service_name_full.split("::").last
83
111
  end
84
112
 
85
113
  def proto_service_file_path
@@ -110,6 +138,10 @@ module Gapic
110
138
  "#{proto_service_name_full}::Stub"
111
139
  end
112
140
 
141
+ def service_name_full
142
+ fix_namespace @api, "#{namespace}::#{name}"
143
+ end
144
+
113
145
  def service_file_path
114
146
  service_require + ".rb"
115
147
  end
@@ -123,7 +155,7 @@ module Gapic
123
155
  end
124
156
 
125
157
  def service_require
126
- ruby_file_path @api, proto_service_name_full
158
+ ruby_file_path @api, service_name_full
127
159
  end
128
160
 
129
161
  def client_name
@@ -131,7 +163,7 @@ module Gapic
131
163
  end
132
164
 
133
165
  def client_name_full
134
- fix_namespace @api, "#{proto_service_name_full}::#{client_name}"
166
+ fix_namespace @api, "#{service_name_full}::#{client_name}"
135
167
  end
136
168
 
137
169
  def create_client_call
@@ -155,16 +187,17 @@ module Gapic
155
187
  end
156
188
 
157
189
  def client_endpoint
158
- return @parent_service.client_endpoint if @parent_service
159
- @service.host || default_config(:default_host) || "localhost"
190
+ @parent_service&.client_endpoint ||
191
+ common_service_delegate&.client_endpoint ||
192
+ @service.host ||
193
+ default_config(:default_host) ||
194
+ "localhost"
160
195
  end
161
196
 
162
197
  def client_scopes
163
- @service.scopes || default_config(:oauth_scopes)
164
- end
165
-
166
- def client_proto_name
167
- @service.address.join "."
198
+ common_service_delegate&.client_scopes ||
199
+ @service.scopes ||
200
+ default_config(:oauth_scopes)
168
201
  end
169
202
 
170
203
  def credentials_name
@@ -172,7 +205,7 @@ module Gapic
172
205
  end
173
206
 
174
207
  def credentials_name_full
175
- fix_namespace @api, "#{proto_service_name_full}::#{credentials_name}"
208
+ fix_namespace @api, "#{service_name_full}::#{credentials_name}"
176
209
  end
177
210
 
178
211
  def credentials_class_xref
@@ -200,7 +233,7 @@ module Gapic
200
233
  end
201
234
 
202
235
  def helpers_require
203
- ruby_file_path @api, "#{proto_service_name_full}::Helpers"
236
+ ruby_file_path @api, "#{service_name_full}::Helpers"
204
237
  end
205
238
 
206
239
  def references
@@ -216,7 +249,7 @@ module Gapic
216
249
  end
217
250
 
218
251
  def paths_name_full
219
- fix_namespace @api, "#{proto_service_name_full}::#{paths_name}"
252
+ fix_namespace @api, "#{service_name_full}::#{paths_name}"
220
253
  end
221
254
 
222
255
  def paths_file_path
@@ -228,13 +261,17 @@ module Gapic
228
261
  end
229
262
 
230
263
  def paths_require
231
- ruby_file_path @api, "#{proto_service_name_full}::#{paths_name}"
264
+ ruby_file_path @api, "#{service_name_full}::#{paths_name}"
232
265
  end
233
266
 
234
267
  def test_client_file_path
235
268
  service_file_path.sub ".rb", "_test.rb"
236
269
  end
237
270
 
271
+ def test_paths_file_path
272
+ service_file_path.sub ".rb", "_paths_test.rb"
273
+ end
274
+
238
275
  def test_client_operations_file_path
239
276
  service_file_path.sub ".rb", "_operations_test.rb"
240
277
  end
@@ -260,7 +297,7 @@ module Gapic
260
297
  end
261
298
 
262
299
  def operations_name_full
263
- fix_namespace @api, "#{proto_service_name_full}::#{operations_name}"
300
+ fix_namespace @api, "#{service_name_full}::#{operations_name}"
264
301
  end
265
302
 
266
303
  def operations_file_path
@@ -272,7 +309,7 @@ module Gapic
272
309
  end
273
310
 
274
311
  def operations_require
275
- ruby_file_path @api, "#{proto_service_name_full}::#{operations_name}"
312
+ ruby_file_path @api, "#{service_name_full}::#{operations_name}"
276
313
  end
277
314
 
278
315
  def lro_service
@@ -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
@@ -91,6 +91,13 @@ module Gapic
91
91
  configuration[:overrides][:namespace].fetch str, str
92
92
  end
93
93
 
94
+ def fix_service_name str
95
+ str = String str
96
+ return str if configuration[:overrides].nil?
97
+ return str if configuration[:overrides][:service].nil?
98
+ configuration[:overrides][:service].fetch str, str
99
+ end
100
+
94
101
  def generate_files
95
102
  @files.select(&:generate?)
96
103
  end
@@ -202,7 +209,7 @@ module Gapic
202
209
  config = config_file ? YAML.load_file(config_file) : {}
203
210
  protoc_options.each do |k, v|
204
211
  next if k == "configuration"
205
- branch = key_to_str(k).split(".").reverse.inject(v) { |m, s| { str_to_key(s) => m } }
212
+ branch = parse_key(key_to_str(k)).reverse.inject(v) { |m, s| { str_to_key(s) => m } }
206
213
  config = deep_merge config, branch
207
214
  end
208
215
  config
@@ -225,6 +232,21 @@ module Gapic
225
232
  configuration[:generate_path_helpers_output] ||= false
226
233
  end
227
234
 
235
+ # Whether the override_proto_namespaces parameter was given in the configuration
236
+ def override_proto_namespaces_defined?
237
+ configuration.key? :override_proto_namespaces
238
+ end
239
+
240
+ # Sets the override_proto_namespaces parameter in the configuration
241
+ def override_proto_namespaces= value
242
+ configuration[:override_proto_namespaces] = value
243
+ end
244
+
245
+ # Whether namespace overrides apply to proto/grpc class references
246
+ def override_proto_namespaces?
247
+ configuration[:override_proto_namespaces] ||= false
248
+ end
249
+
228
250
  # Raw parsed json of the combined grpc service config files if provided
229
251
  # or an empty hash if no config was provided
230
252
  def grpc_service_config_raw
@@ -249,6 +271,28 @@ module Gapic
249
271
  @resource_types[resource_type]
250
272
  end
251
273
 
274
+ # Given a service, find all common services that use it as a delegate.
275
+ def common_services_for delegate
276
+ @delegate_to_common ||= begin
277
+ (configuration[:common_services] || {}).each_with_object({}) do |(c, d), mapping|
278
+ (mapping[d] ||= []) << c
279
+ end
280
+ end
281
+ all_services = services
282
+ @delegate_to_common.fetch(delegate.address.join("."), []).map do |addr|
283
+ addr = addr.split "."
284
+ all_services.find { |s| s.address == addr }
285
+ end.compact.uniq
286
+ end
287
+
288
+ # Given a common service, return its delegate.
289
+ def delegate_service_for common
290
+ addr = (configuration[:common_services] || {})[common.address.join "."]
291
+ return nil unless addr
292
+ addr = addr.split "."
293
+ services.find { |s| s.address == addr }
294
+ end
295
+
252
296
  private
253
297
 
254
298
  # Does a pre-analysis of all resources defined in the job. This has
@@ -291,6 +335,8 @@ module Gapic
291
335
  end
292
336
  end
293
337
 
338
+ # Parse a comma-delimited list of equals-delimited lists of strings, while
339
+ # mapping backslash-escaped commas and equal signs to literal characters.
294
340
  def parse_parameter str
295
341
  str.scan(/\\.|,|=|[^\\,=]+/)
296
342
  .each_with_object([[String.new]]) do |tok, arr|
@@ -307,6 +353,22 @@ module Gapic
307
353
  end
308
354
  end
309
355
 
356
+ # split the string on periods, but map backslash-escaped periods to
357
+ # literal periods.
358
+ def parse_key str
359
+ str.scan(/\.|\\.|[^\.\\]+/)
360
+ .each_with_object([String.new]) do |tok, arr|
361
+ if tok == "."
362
+ arr.append String.new
363
+ elsif tok.start_with? "\\"
364
+ arr.last << tok[1]
365
+ else
366
+ arr.last << tok
367
+ end
368
+ arr
369
+ end
370
+ end
371
+
310
372
  def str_to_key str
311
373
  str = str.to_s
312
374
  str.start_with?(":") ? str[1..-1].to_sym : str
@@ -2,3 +2,6 @@
2
2
  source "https://rubygems.org"
3
3
 
4
4
  gemspec
5
+
6
+ # google-protobuf 3.12.0 requires Ruby 2.5 or later, so pin to 3.11 on older Rubies
7
+ gem "google-protobuf", (RUBY_VERSION < "2.5" ? "~> 3.11.4" : "~> 3.12")
@@ -29,7 +29,9 @@ Gem::Specification.new do |gem|
29
29
  <%- end -%>
30
30
 
31
31
  gem.add_development_dependency "google-style", "~> 1.24.0"
32
- gem.add_development_dependency "minitest", "~> 5.10"
32
+ gem.add_development_dependency "minitest", "~> 5.14"
33
+ gem.add_development_dependency "minitest-focus", "~> 1.1"
34
+ gem.add_development_dependency "minitest-rg", "~> 5.2"
33
35
  gem.add_development_dependency "rake", ">= 12.0"
34
36
  gem.add_development_dependency "redcarpet", "~> 3.0"
35
37
  gem.add_development_dependency "simplecov", "~> 0.18"
@@ -10,6 +10,7 @@ RuboCop::RakeTask.new
10
10
  require "rake/testtask"
11
11
  desc "Run tests."
12
12
  Rake::TestTask.new do |t|
13
+ t.libs << "test"
13
14
  t.test_files = FileList["test/**/*_test.rb"]
14
15
  t.warning = false
15
16
  end
@@ -0,0 +1,8 @@
1
+ <%- assert_locals gem -%>
2
+ <%= render partial: "shared/header" -%>
3
+
4
+ require "simplecov"
5
+
6
+ require "minitest/autorun"
7
+ require "minitest/focus"
8
+ require "minitest/rg"
@@ -5,12 +5,13 @@
5
5
  <%= @requires -%>
6
6
  <%- end -%>
7
7
 
8
- <%- namespace.split("::").each_with_index do |ns, i| -%>
8
+ <%- namespace_components = namespace.split("::").reject(&:empty?) -%>
9
+ <%- namespace_components.each_with_index do |ns, i| -%>
9
10
  <%= indent "module #{ns}", i*2 %>
10
11
  <%- end -%>
11
- <%= indent yield, namespace.split("::").count*2 %>
12
- <%- namespace.split("::").count.times do |i| -%>
13
- <%= indent "end", (namespace.split("::").count-1-i)*2 %>
12
+ <%= indent yield, namespace_components.count*2 %>
13
+ <%- namespace_components.count.times do |i| -%>
14
+ <%= indent "end", (namespace_components.count-1-i)*2 %>
14
15
  <%- end -%>
15
16
  <%- if instance_variable_defined? :@footer -%>
16
17
 
@@ -10,8 +10,8 @@
10
10
  <%- end -%>
11
11
  <%- end -%>
12
12
  class <%= message.name %>
13
- include Google::Protobuf::MessageExts
14
- extend Google::Protobuf::MessageExts::ClassMethods
13
+ include ::Google::Protobuf::MessageExts
14
+ extend ::Google::Protobuf::MessageExts::ClassMethods
15
15
  <%- message.nested_messages.each do |submsg| -%>
16
16
 
17
17
  <%= indent_tail render(partial: "proto_docs/message", locals: { message: submsg }), 2 %>
@@ -2,5 +2,5 @@
2
2
  <%= render partial: "service/client/client",
3
3
  layout: "layouts/ruby",
4
4
  locals: { service: service,
5
- namespace: service.proto_service_name_full }
5
+ namespace: service.service_name_full }
6
6
  %>
@@ -29,7 +29,7 @@ class <%= service.client_name %>
29
29
  # To modify the configuration for all <%= service.name %> clients:
30
30
  #
31
31
  # <%= service.client_name_full %>.configure do |config|
32
- # config.timeout = 10_000
32
+ # config.timeout = 10.0
33
33
  # end
34
34
  #
35
35
  # @yield [config] Configure the <%= service.client_name %> client.
@@ -75,7 +75,7 @@ class <%= service.client_name %>
75
75
  # configuration:
76
76
  #
77
77
  # client = <%= service.client_name_full %>.new do |config|
78
- # config.timeout = 10_000
78
+ # config.timeout = 10.0
79
79
  # end
80
80
  #
81
81
  # @yield [config] Configure the <%= service.name %> client.
@@ -100,15 +100,17 @@ class <%= service.client_name %>
100
100
  if credentials.is_a?(String) || credentials.is_a?(Hash)
101
101
  credentials = Credentials.new credentials, scope: @config.scope
102
102
  end
103
- @quota_project_id = credentials.respond_to?(:quota_project_id) ? credentials.quota_project_id : nil
103
+ @quota_project_id = @config.quota_project
104
+ @quota_project_id ||= credentials.quota_project_id if credentials.respond_to? :quota_project_id
104
105
 
105
106
  <%- if service.lro? -%>
106
107
  <%= service.lro_client_ivar %> = <%= service.operations_name %>.new do |config|
107
108
  config.credentials = credentials
109
+ config.endpoint = @config.endpoint
108
110
  end
109
111
 
110
112
  <%- end -%>
111
- @<%= service.stub_name %> = Gapic::ServiceStub.new(
113
+ @<%= service.stub_name %> = ::Gapic::ServiceStub.new(
112
114
  <%= service.proto_service_stub_name_full %>,
113
115
  credentials: credentials,
114
116
  endpoint: @config.endpoint,
@@ -117,6 +119,15 @@ class <%= service.client_name %>
117
119
  )
118
120
  end
119
121
 
122
+ <%- if service.lro? -%>
123
+ ##
124
+ # Get the associated client for long-running operations.
125
+ #
126
+ # @return [<%= service.operations_name_full %>]
127
+ #
128
+ attr_reader :<%= service.lro_client_var %>
129
+
130
+ <%- end -%>
120
131
  # Service calls
121
132
  <%- service.methods.each do |method| -%>
122
133