gapic-generator 0.52.0 → 0.53.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/lib/gapic/generator/version.rb +1 -1
  4. data/lib/gapic/generators/default_generator.rb +6 -0
  5. data/lib/gapic/model/method/resumable_upload.rb +143 -0
  6. data/lib/gapic/presenters/gem_presenter.rb +1 -1
  7. data/lib/gapic/presenters/method_presenter.rb +35 -0
  8. data/lib/gapic/presenters/service_presenter.rb +65 -0
  9. data/lib/gapic/presenters/service_rest_presenter.rb +12 -0
  10. data/lib/gapic/presenters/snippet/response_handling_presenters.rb +60 -0
  11. data/lib/gapic/presenters/snippet_presenter.rb +16 -6
  12. data/templates/default/lib/_service.text.erb +3 -0
  13. data/templates/default/lib/rest/_rest.text.erb +3 -0
  14. data/templates/default/service/client/_client.text.erb +18 -0
  15. data/templates/default/service/client/_config.text.erb +8 -0
  16. data/templates/default/service/client/method/_def.text.erb +5 -1
  17. data/templates/default/service/client/method/def/_options_defaults.text.erb +5 -0
  18. data/templates/default/service/client/method/def/_response.text.erb +3 -1
  19. data/templates/default/service/client/method/def/_response_resumable_upload.text.erb +13 -0
  20. data/templates/default/service/client/method/def/_upload_error_handler.text.erb +2 -0
  21. data/templates/default/service/client/method/docs/_request_normal.text.erb +19 -0
  22. data/templates/default/service/client/method/docs/_response.text.erb +8 -0
  23. data/templates/default/service/rest/client/_client.text.erb +18 -0
  24. data/templates/default/service/rest/client/_config.text.erb +8 -0
  25. data/templates/default/service/rest/client/method/_def.text.erb +5 -1
  26. data/templates/default/service/rest/client/method/def/_response.text.erb +3 -1
  27. data/templates/default/service/rest/client/method/def/_response_resumable_upload.text.erb +13 -0
  28. data/templates/default/service/rest/client/method/def/_upload_error_handler.text.erb +2 -0
  29. data/templates/default/service/rest/client/method/docs/_request.text.erb +19 -0
  30. data/templates/default/service/rest/client/method/docs/_result.text.erb +7 -1
  31. data/templates/default/service/rest/service_stub/_service_stub.text.erb +2 -2
  32. data/templates/default/service/rest/service_stub/grpc_transcoding_method/_bindings.text.erb +15 -0
  33. data/templates/default/service/rest/service_stub/grpc_transcoding_method/_def.text.erb +1 -14
  34. data/templates/default/service/rest/test/client.text.erb +1 -1
  35. data/templates/default/service/resumable_upload_stub/_resumable_upload_stub.text.erb +64 -0
  36. data/templates/default/service/resumable_upload_stub/_transcoding_method.text.erb +22 -0
  37. data/templates/default/service/resumable_upload_stub.text.erb +6 -0
  38. data/templates/default/service/test/client.text.erb +1 -1
  39. data/templates/default/service/test/resumable_upload.text.erb +132 -0
  40. metadata +11 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b45efa15311119a19ee117f8eaf8fa2af651151a80f8ed004373b232652f599f
4
- data.tar.gz: 69203b61e070ca8a4a02e901299739754f1048aa935025fd9ac53af072a5aac7
3
+ metadata.gz: 3264cfe286069bf2e3bed64ae2246c0b8e48d97851809c99c5520e072fa38889
4
+ data.tar.gz: d9503be57f76aedd856a46159e138673fc83f2912fa51018d16e5cd1e8c71f90
5
5
  SHA512:
6
- metadata.gz: 10fbd033561469722836fbbb60888097f91571179880221a033aff5e14be7e980c6a5e94e8383305a7422df16d7158ecf39f54a312c742ea642892927d7b248a
7
- data.tar.gz: 5718898dd81be9b2b3eedf1dc188f61821f2d2d012daf12a82510ba1b09fd696c4594b6dbedee6c19b31ec64da5f15bca4c0ce4a98749c20e1f80aa64975aa8c
6
+ metadata.gz: d8109c1036d6b95c2bf2fe201de7e2cf26516e4d315c6d4ca3331b52f0d312d27884416bd6ce7db8209ae78c13ffab4120b444e229fc914d3d329ca801dd6837
7
+ data.tar.gz: 1ef6ae68e1fc76dfc19bbe947d7f4340918403494d57bd3d1ba40540b953921470a5090b7ed7f6d2fe80519066c28d92e8a0a3115acbf4d498fc43c8580debd9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Release History for gapic-generator
2
2
 
3
+ ### 0.53.0 / 2026-09-30
4
+
5
+ * Feature: update gapic-common for generated libraries to 1.4
6
+ * Feature: resumable upload support in gapic-generator
7
+
3
8
  ### 0.52.0 / 2026-09-04
4
9
 
5
10
  * Fix: escape multi-line braces and backtick unknown doc tags in yard formatting
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Gapic
18
18
  module Generator
19
- VERSION = "0.52.0"
19
+ VERSION = "0.53.0"
20
20
  end
21
21
  end
@@ -92,10 +92,16 @@ module Gapic
92
92
  # Rest-only `service.stub` file
93
93
  files << g("service/rest/service_stub", "lib/#{service.rest.service_stub_file_path}", service: service) if should_generate_rest
94
94
 
95
+ # Resumable upload stub, shared by both transports because uploads always travel over REST
96
+ files << g("service/resumable_upload_stub", "lib/#{service.resumable_upload_stub_file_path}", service: service) if service.resumable_upload?
97
+
95
98
  # Unit tests for `client.rb`
96
99
  files << g("service/test/client", "test/#{service.test_client_file_path}", service: service) if should_generate_grpc
97
100
  files << g("service/rest/test/client", "test/#{service.rest.test_client_file_path}", service: service) if should_generate_rest
98
101
 
102
+ # Unit tests for resumable upload RPCs, which the client tests above skip
103
+ files << g("service/test/resumable_upload", "test/#{service.test_resumable_upload_file_path}", service: service) if service.resumable_upload?
104
+
99
105
  # Unit tests for `paths.rb`
100
106
  files << g("service/test/client_paths", "test/#{service.test_paths_file_path}", service: service) if service.paths? && should_generate_grpc
101
107
 
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2026 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
+ require "gapic/model/model_error"
18
+
19
+ module Gapic
20
+ module Model
21
+ module Method
22
+ ##
23
+ # Resumable upload method-level model.
24
+ #
25
+ # A resumable upload RPC does not send its payload in the initiation request. The request it
26
+ # describes only creates an upload session; the bytes travel afterwards, over REST, in chunks
27
+ # addressed to a URL the server hands back. Generated clients therefore return an upload handle
28
+ # from such a method rather than a response message.
29
+ #
30
+ # Until the upload annotation is published, the set of such RPCs is carried here as a table, and
31
+ # so is the URL prefix each one's initiation request is sent under. When the annotation lands,
32
+ # {.url_prefix_for} keeps the table and detection moves to `http.media_upload.enabled`.
33
+ #
34
+ # @!attribute [r] url_prefix
35
+ # @return [String] The path prefix prepended to the transcoded initiation URL, without
36
+ # surrounding slashes, e.g. `resumable/upload`.
37
+ #
38
+ class ResumableUpload
39
+ # @return [String]
40
+ attr_reader :url_prefix
41
+
42
+ ##
43
+ # @param url_prefix [String] The upload URL prefix for the matched RPC.
44
+ #
45
+ def initialize url_prefix
46
+ @url_prefix = url_prefix
47
+ end
48
+
49
+ ##
50
+ # Exact matches, keyed by the full gRPC name of the RPC.
51
+ #
52
+ EXACT_PREFIXES = {
53
+ "google.showcase.v1beta1.ResumableUploadService.UploadMedia" => "resumable/upload"
54
+ }.freeze
55
+
56
+ ##
57
+ # Version-family matches, for protos that are republished under a new version regularly.
58
+ # Anchored on the left at the package and on the right at the service and method, with the
59
+ # intervening segments (e.g. `.services.`) unconstrained.
60
+ #
61
+ VERSIONED_PREFIXES = [
62
+ {
63
+ left: /\Agoogle\.ads\.googleads\.v[0-9_]+\./,
64
+ right: ".YouTubeVideoUploadService.CreateYouTubeVideoUpload",
65
+ prefix: "resumable/upload"
66
+ }
67
+ ].freeze
68
+
69
+ class << self
70
+ ##
71
+ # Inspects a method and returns its resumable upload model, or `nil` if it does not perform
72
+ # resumable uploads.
73
+ #
74
+ # @param method [Gapic::Presenters::MethodPresenter]
75
+ #
76
+ # @raise [Gapic::Model::ModelError] if the method is a resumable upload RPC that the
77
+ # generator cannot generate an upload surface for.
78
+ #
79
+ # @return [Gapic::Model::Method::ResumableUpload, nil]
80
+ #
81
+ def create method
82
+ prefix = url_prefix_for method.grpc_full_name
83
+ return nil if prefix.nil?
84
+ validate! method
85
+ new prefix
86
+ end
87
+
88
+ ##
89
+ # The upload URL prefix for an RPC, or `nil` if the RPC does not perform resumable uploads.
90
+ #
91
+ # @param full_name [String] The full gRPC name of the RPC,
92
+ # e.g. `google.showcase.v1beta1.ResumableUploadService.UploadMedia`.
93
+ #
94
+ # @return [String, nil]
95
+ #
96
+ def url_prefix_for full_name
97
+ EXACT_PREFIXES[full_name] ||
98
+ VERSIONED_PREFIXES.find do |match|
99
+ match[:left].match?(full_name) && full_name.end_with?(match[:right])
100
+ end&.fetch(:prefix)
101
+ end
102
+
103
+ ##
104
+ # Verifies that an upload surface can be generated for the given method. A resumable upload
105
+ # is a single unary POST that carries a body, and anything else in the table is a
106
+ # misconfiguration that must fail the build rather than generate code that cannot work.
107
+ #
108
+ # @param method [Gapic::Presenters::MethodPresenter]
109
+ #
110
+ # @raise [Gapic::Model::ModelError]
111
+ #
112
+ # @return [void]
113
+ #
114
+ def validate! method
115
+ reason = unsupported_reason method
116
+ return if reason.nil?
117
+ raise ModelError, "The method #{method.grpc_full_name} performs resumable uploads, " \
118
+ "which the generator supports only for #{reason}."
119
+ end
120
+
121
+ private
122
+
123
+ ##
124
+ # @param method [Gapic::Presenters::MethodPresenter]
125
+ # @return [String, nil] What the method would have had to be, or `nil` if it is supported.
126
+ #
127
+ def unsupported_reason method
128
+ return "non-streaming methods" if method.client_streaming? || method.server_streaming?
129
+ return "non-paginated methods" if method.paged?
130
+ return "methods that are not long-running operations" if method.lro? || method.nonstandard_lro?
131
+
132
+ binding = method.http_bindings.first
133
+ return "methods with an HTTP binding" if binding.nil?
134
+ return "methods bound to POST" unless binding.verb == :post
135
+ return "methods whose HTTP binding has a body" unless binding.body?
136
+
137
+ nil
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -260,7 +260,7 @@ module Gapic
260
260
 
261
261
  def dependencies
262
262
  @dependencies ||= begin
263
- deps = { "gapic-common" => "~> 1.3" }
263
+ deps = { "gapic-common" => "~> 1.5" }
264
264
  deps["grpc-google-iam-v1"] = "~> 1.11" if iam_dependency?
265
265
  extra_deps = gem_config_dependencies
266
266
  deps.merge! mixins_model.dependencies if mixins_model.mixins?
@@ -17,6 +17,7 @@
17
17
  require "active_support/inflector"
18
18
  require "gapic/ruby_info"
19
19
  require "gapic/helpers/namespace_helper"
20
+ require "gapic/model/method/resumable_upload"
20
21
 
21
22
  module Gapic
22
23
  module Presenters
@@ -71,6 +72,10 @@ module Gapic
71
72
  @lro = Gapic::Model::Method.parse_lro @method, @api
72
73
 
73
74
  @rest = MethodRestPresenter.new self, @api
75
+
76
+ # Built last: detection is cheap but its validation reads the LRO model, the HTTP bindings
77
+ # and the pagination check, all of which have to exist first.
78
+ @resumable_upload = Gapic::Model::Method::ResumableUpload.create self
74
79
  end
75
80
 
76
81
  ##
@@ -279,6 +284,36 @@ module Gapic
279
284
  service.nonstandard_lros.find { |model| model.service == @lro.service_full_name }
280
285
  end
281
286
 
287
+ ##
288
+ # Whether this method performs a resumable upload. Such a method returns an upload handle
289
+ # rather than a response, and its payload travels over REST in chunks after the request this
290
+ # method describes has created the upload session.
291
+ #
292
+ # @return [Boolean]
293
+ #
294
+ def resumable_upload?
295
+ !@resumable_upload.nil?
296
+ end
297
+
298
+ ##
299
+ # The path prefix prepended to this method's transcoded initiation URL, without surrounding
300
+ # slashes, e.g. `resumable/upload`. `nil` unless this method performs a resumable upload.
301
+ #
302
+ # @return [String, nil]
303
+ #
304
+ def upload_url_prefix
305
+ @resumable_upload&.url_prefix
306
+ end
307
+
308
+ ##
309
+ # The name of the constant the generated upload stub holds this method's URL prefix in.
310
+ #
311
+ # @return [String]
312
+ #
313
+ def upload_url_prefix_const_name
314
+ "#{name.upcase}_URL_PREFIX"
315
+ end
316
+
282
317
  def client_streaming?
283
318
  @method.client_streaming
284
319
  end
@@ -422,6 +422,15 @@ module Gapic
422
422
  service_file_path.sub ".rb", "_operations_test.rb"
423
423
  end
424
424
 
425
+ ##
426
+ # Path of the generated tests covering this service's resumable upload RPCs. Those RPCs are
427
+ # excluded from the ordinary client tests, which assume a call returns a response.
428
+ #
429
+ # @return [String]
430
+ def test_resumable_upload_file_path
431
+ service_file_path.sub ".rb", "_resumable_upload_test.rb"
432
+ end
433
+
425
434
  def stub_name
426
435
  "#{ActiveSupport::Inflector.underscore name}_stub"
427
436
  end
@@ -489,6 +498,62 @@ module Gapic
489
498
  ServicePresenter.new @gem_presenter, @api, lro.services.first, parent_service: self unless lro.nil?
490
499
  end
491
500
 
501
+ ##
502
+ # Whether any of this service's RPCs perform resumable uploads, and therefore whether an upload
503
+ # stub has to be generated for it and built by its clients.
504
+ #
505
+ # @return [Boolean]
506
+ def resumable_upload?
507
+ methods.any?(&:resumable_upload?)
508
+ end
509
+
510
+ ##
511
+ # Presenters for the RPCs of this service that perform resumable uploads.
512
+ #
513
+ # @return [Enumerable<Gapic::Presenters::MethodPresenter>]
514
+ def resumable_upload_methods
515
+ methods.select(&:resumable_upload?)
516
+ end
517
+
518
+ ##
519
+ # The class name of the generated upload stub. One per service, shared by both transports, and
520
+ # deliberately not nested under `Rest::`: the gRPC client builds it too, because the upload
521
+ # itself always travels over REST.
522
+ #
523
+ # @return [String]
524
+ def resumable_upload_stub_name
525
+ "ResumableUploadStub"
526
+ end
527
+
528
+ # @return [String]
529
+ def resumable_upload_stub_name_full
530
+ fix_namespace @api, "#{service_name_full}::#{resumable_upload_stub_name}"
531
+ end
532
+
533
+ # @return [String]
534
+ def resumable_upload_stub_require
535
+ ruby_file_path @api, resumable_upload_stub_name_full
536
+ end
537
+
538
+ # @return [String]
539
+ def resumable_upload_stub_file_path
540
+ "#{resumable_upload_stub_require}.rb"
541
+ end
542
+
543
+ # @return [String]
544
+ def resumable_upload_stub_file_name
545
+ resumable_upload_stub_file_path.split("/").last
546
+ end
547
+
548
+ ##
549
+ # An instance variable name used for the generated upload stub. The clients keep the stub here
550
+ # and expose no reader for it.
551
+ #
552
+ # @return [String]
553
+ def resumable_upload_stub_ivar
554
+ "@resumable_upload_stub"
555
+ end
556
+
492
557
  def config_channel_args
493
558
  { "grpc.service_config_disable_resolution" => 1 }
494
559
  end
@@ -357,6 +357,18 @@ module Gapic
357
357
  main_service.methods.select(&:can_generate_rest?)
358
358
  end
359
359
 
360
+ ##
361
+ # Presenters for methods that the REST service stub carries an implementation for. An upload
362
+ # RPC has no ordinary REST path: the REST client delegates to the upload handle exactly as the
363
+ # gRPC client does, so a plain call method and transcoder here would be dead code that also
364
+ # happens to be wrong — a non-resumable POST of the whole payload.
365
+ #
366
+ # @return [Enumerable<Gapic::Presenters::MethodPresenter>]
367
+ #
368
+ def service_stub_methods
369
+ methods.reject(&:resumable_upload?)
370
+ end
371
+
360
372
  ##
361
373
  # Require string for the helpers file
362
374
  #
@@ -80,6 +80,66 @@ module Gapic
80
80
  attr_reader :response_name
81
81
  end
82
82
 
83
+ ##
84
+ # Presentation information about resumable upload response handling.
85
+ #
86
+ # A resumable upload RPC returns a {::Gapic::ResumableUpload} handle rather than a
87
+ # response message, so the snippet names the call result `upload` and goes on to
88
+ # start the upload, which is what actually produces the response message.
89
+ #
90
+ class ResumableUploadResponseHandlingPresenter
91
+ include ResponseHandlingPresenterCommon
92
+
93
+ ##
94
+ # Create a resumable upload response handling presenter
95
+ #
96
+ # @param proto [Google::Cloud::Tools::SnippetGen::ConfigLanguage::V1::Snippet::SimpleResponseHandling]
97
+ # The protobuf representation
98
+ # @param json [String]
99
+ # The JSON representation
100
+ # @param response_type [String] The fully qualified response message class
101
+ # @param phase1 [Boolean] True if this is a phase 1 snippet without config
102
+ #
103
+ def initialize proto, _json, response_type:, phase1:
104
+ @response_name = phase1 ? "upload" : compute_response_name(proto, phase1)
105
+ @render_lines = phase1 ? upload_lines(response_type) : []
106
+ @render = @render_lines.join "\n"
107
+ end
108
+
109
+ ##
110
+ # The lines of rendered code
111
+ # @return [Array<String>]
112
+ #
113
+ attr_reader :render_lines
114
+
115
+ ##
116
+ # The rendered code as a single string, possibly with line breaks
117
+ # @return [String]
118
+ #
119
+ attr_reader :render
120
+
121
+ ##
122
+ # The name of the response variable, or nil for no response handling
123
+ # @return [String,nil]
124
+ #
125
+ attr_reader :response_name
126
+
127
+ private
128
+
129
+ def upload_lines response_type
130
+ [
131
+ "# The returned object is a handle for a resumable upload. Nothing has been",
132
+ "# uploaded yet, and the timeout and retry policy of the call above cover only",
133
+ "# the request that creates the upload session, not the upload as a whole.",
134
+ "stream = File.open \"input.bin\", \"rb\"",
135
+ "result = #{@response_name}.start stream: stream, content_type: \"application/octet-stream\"",
136
+ "",
137
+ "# The returned object is of type #{response_type}.",
138
+ "p result"
139
+ ]
140
+ end
141
+ end
142
+
83
143
  ##
84
144
  # Presentation information about LRO response handling
85
145
  #
@@ -63,6 +63,8 @@ module Gapic
63
63
  :paged
64
64
  elsif @method_presenter.lro?
65
65
  :lro
66
+ elsif @method_presenter.resumable_upload?
67
+ :resumable_upload
66
68
  else
67
69
  :simple
68
70
  end
@@ -244,13 +246,12 @@ module Gapic
244
246
  LroResponseHandlingPresenter.new call_proto&.lro_handling,
245
247
  call_json&.fetch("lroHandling", nil),
246
248
  phase1: phase1
249
+ when :resumable_upload
250
+ ResumableUploadResponseHandlingPresenter.new call_proto&.response_handling,
251
+ call_json&.fetch("responseHandling", nil),
252
+ response_type: return_type, phase1: phase1
247
253
  when :streaming
248
- response_name = phase1 ? "output" : call_proto&.server_stream_name
249
- response_name = nil if response_name == ""
250
- StreamingResponseHandlingPresenter.new call_proto&.response_handling,
251
- call_json&.fetch("responseHandling", nil),
252
- response_name: response_name, base_response_type: base_response_type,
253
- phase1: phase1
254
+ build_streaming_response_handling_presenter call_proto, call_json, phase1
254
255
  else
255
256
  SimpleResponseHandlingPresenter.new call_proto&.response_handling,
256
257
  call_json&.fetch("responseHandling", nil),
@@ -258,6 +259,15 @@ module Gapic
258
259
  end
259
260
  end
260
261
 
262
+ def build_streaming_response_handling_presenter call_proto, call_json, phase1
263
+ response_name = phase1 ? "output" : call_proto&.server_stream_name
264
+ response_name = nil if response_name == ""
265
+ StreamingResponseHandlingPresenter.new call_proto&.response_handling,
266
+ call_json&.fetch("responseHandling", nil),
267
+ response_name: response_name, base_response_type: base_response_type,
268
+ phase1: phase1
269
+ end
270
+
261
271
  def build_client_call_presenter call_proto, call_json, request_name, response_name
262
272
  phase1 = !config?
263
273
  if response_kind == :paged
@@ -15,6 +15,9 @@ require "<%= service.credentials_require %>"
15
15
  <%- if service.paths? -%>
16
16
  require "<%= service.paths_require %>"
17
17
  <%- end -%>
18
+ <%- if service.resumable_upload? -%>
19
+ require "<%= service.resumable_upload_stub_require %>"
20
+ <%- end -%>
18
21
  <%- if service.generate_grpc_clients? -%>
19
22
  <%- if service.lro? -%>
20
23
  require "<%= service.operations_require %>"
@@ -15,6 +15,9 @@ require "<%= service.credentials_require %>"
15
15
  <%- if service.paths? -%>
16
16
  require "<%= service.paths_require %>"
17
17
  <%- end -%>
18
+ <%- if service.resumable_upload? -%>
19
+ require "<%= service.resumable_upload_stub_require %>"
20
+ <%- end -%>
18
21
  <%- if service.rest.lro? -%>
19
22
  require "<%= service.rest.operations_require %>"
20
23
  <%- end -%>
@@ -20,6 +20,14 @@ require "securerandom"
20
20
  <%= indent service.doc_description(transport: :grpc), "# " %>
21
21
  #
22
22
  <%- end -%>
23
+ <%- if service.resumable_upload? -%>
24
+ <%- upload_names = service.resumable_upload_methods.map { |upload_method| "`#{upload_method.name}`" }.join ", " -%>
25
+ <%- upload_many = service.resumable_upload_methods.length > 1 -%>
26
+ # <%= upload_names %> <%= upload_many ? "perform resumable uploads: each returns" : "performs a resumable upload: it returns" %> a {::Gapic::ResumableUpload} handle
27
+ # instead of a response, and the per-call `timeout` and retry policy cover only the request that
28
+ # creates the upload session, not the upload itself.
29
+ #
30
+ <%- end -%>
23
31
  class <%= service.client_name %>
24
32
  # @private
25
33
  API_VERSION = "<%= service.api_version %>".freeze
@@ -140,6 +148,16 @@ class <%= service.client_name %>
140
148
  config.universe_domain = @config.universe_domain
141
149
  end
142
150
 
151
+ <%- end -%>
152
+ <%- if service.resumable_upload? -%>
153
+ <%= service.resumable_upload_stub_ivar %> = <%= service.resumable_upload_stub_name_full %>.new(
154
+ endpoint: @config.endpoint,
155
+ endpoint_template: DEFAULT_ENDPOINT_TEMPLATE,
156
+ universe_domain: @config.universe_domain,
157
+ credentials: credentials,
158
+ logger: @config.logger
159
+ )
160
+
143
161
  <%- end -%>
144
162
  @<%= service.stub_name %> = ::Gapic::ServiceStub.new(
145
163
  <%= service.proto_service_stub_name_full %>,
@@ -190,6 +190,14 @@ class Configuration
190
190
  <%- method_service.methods.each do |method| -%>
191
191
  ##
192
192
  # RPC-specific configuration for `<%= method.name %>`
193
+ <%- if method.resumable_upload? -%>
194
+ #
195
+ # `<%= method.name %>` performs a resumable upload, so the `timeout` and `retry_policy`
196
+ # configured here cover the request that creates the upload session only, not the upload
197
+ # itself. The whole-upload budget is the `upload_timeout:` argument of
198
+ # {::Gapic::ResumableUpload#start} and {::Gapic::ResumableUpload#resume}.
199
+ #
200
+ <%- end -%>
193
201
  # @return [::Gapic::Config::Method]
194
202
  #
195
203
  attr_reader :<%= method.name %>
@@ -9,15 +9,19 @@
9
9
  #
10
10
  <%= render partial: "service/client/method/docs/response", locals: { method: method } -%>
11
11
  #
12
+ <%- unless method.resumable_upload? -%>
12
13
  <%= render partial: "service/client/method/docs/error", locals: { method: method } -%>
13
14
  #
15
+ <%- end -%>
14
16
  <%= render partial: "service/client/method/docs/snippets", locals: { method: method } -%>
15
17
  <%= render partial: "service/client/method/docs/samples", locals: { method: method } -%>
16
- def <%= method.name %> request, options = nil
18
+ def <%= method.name %> request<%= method.resumable_upload? ? " = {}" : "" %>, options = nil
17
19
  <%= indent render(partial: "service/client/method/def/request", locals: { method: method }), 2 %>
18
20
 
19
21
  <%= indent render(partial: "service/client/method/def/options_defaults", locals: { method: method }), 2 %>
20
22
 
21
23
  <%= indent render(partial: "service/client/method/def/response", locals: { method: method }), 2 %>
24
+ <%- unless method.resumable_upload? -%>
22
25
  <%= render partial: "service/client/method/def/rescue", locals: { method: method } -%>
26
+ <%- end -%>
23
27
  end
@@ -8,7 +8,12 @@ metadata = @config.rpcs.<%= method.name %>.metadata.to_h
8
8
  # Set x-goog-api-client, x-goog-user-project and x-goog-api-version headers
9
9
  metadata[:"x-goog-api-client"] ||= ::Gapic::Headers.x_goog_api_client \
10
10
  lib_name: @config.lib_name, lib_version: @config.lib_version,
11
+ <%- if method.resumable_upload? -%>
12
+ gapic_version: ::<%= method.service.gem.version_name_full %>,
13
+ transports_version_send: [:rest]
14
+ <%- else -%>
11
15
  gapic_version: ::<%= method.service.gem.version_name_full %>
16
+ <%- end -%>
12
17
  metadata[:"x-goog-api-version"] = API_VERSION unless API_VERSION.empty?
13
18
  metadata[:"x-goog-user-project"] = @quota_project_id if @quota_project_id
14
19
  <%- if method.routing_params? && !method.client_streaming? -%>
@@ -1,5 +1,7 @@
1
1
  <%- assert_locals method -%>
2
- <%- if method.paged? -%>
2
+ <%- if method.resumable_upload? -%>
3
+ <%= render partial: "service/client/method/def/response_resumable_upload", locals: { method: method } -%>
4
+ <%- elsif method.paged? -%>
3
5
  <%= render partial: "service/client/method/def/response_paged", locals: { method: method } -%>
4
6
  <%- elsif method.rest.nonstandard_lro? -%>
5
7
  <%= render partial: "service/client/method/def/response_nonstandard_lro", locals: { method: method } -%>
@@ -0,0 +1,13 @@
1
+ <%- assert_locals method -%>
2
+ <%- error_handler = render(partial: "service/client/method/def/upload_error_handler", locals: { method: method }).strip -%>
3
+ ::Gapic::ResumableUpload.new(
4
+ client_stub_proc: -> { <%= method.service.resumable_upload_stub_ivar %>.client_stub },
5
+ initial_request_proc: lambda {
6
+ <%= method.service.resumable_upload_stub_name_full %>.<%= method.rest.transcoding_helper_name %> request
7
+ },
8
+ initial_headers: options.metadata,
9
+ start_retry_policy: ::Gapic::Rest::ResumableUpload.start_retry_policy_for(options),
10
+ response_type: <%= method.return_type %>,
11
+ method_name: "<%= method.name %>",
12
+ error_handler: <%= error_handler %>
13
+ )
@@ -0,0 +1,2 @@
1
+ <%- assert_locals method -%>
2
+ nil
@@ -1,4 +1,22 @@
1
1
  <%- assert_locals method -%>
2
+ <%- if method.resumable_upload? -%>
3
+ # @overload <%= method.name %>(request = {}, options = nil)
4
+ # Pass arguments to `<%= method.name %>` via a request object, either of type
5
+ # {<%= method.request_type %>} or an equivalent Hash.
6
+ #
7
+ # @param request [<%= method.request_type %>, ::Hash]
8
+ # A request object representing the call parameters. Optional: it is ignored when the returned
9
+ # handle is resumed rather than started, because a resumed upload targets a session the server
10
+ # has already created.
11
+ # @param options [::Gapic::CallOptions, ::Hash]
12
+ # Overrides for the initiation request only. The `timeout`, `retry_policy` and `metadata` set
13
+ # here apply to the single request that creates the upload session, not to the upload as a
14
+ # whole: an upload still transferring bytes an hour later has long outlived this `timeout`.
15
+ # To bound the whole upload, pass `upload_timeout:` to {::Gapic::ResumableUpload#start} or
16
+ # {::Gapic::ResumableUpload#resume}. Chunk transfers are retried by the upload protocol itself,
17
+ # with the defaults documented on {::Gapic::ResumableUpload}; no call option reaches them.
18
+ # Optional.
19
+ <%- else -%>
2
20
  # @overload <%= method.name %>(request, options = nil)
3
21
  # Pass arguments to `<%= method.name %>` via a request object, either of type
4
22
  # {<%= method.request_type %>} or an equivalent Hash.
@@ -8,6 +26,7 @@
8
26
  # parameters, or to keep all the default parameter values, pass an empty Hash.
9
27
  # @param options [::Gapic::CallOptions, ::Hash]
10
28
  # Overrides the default settings for this call, e.g, timeout, retries, etc. Optional.
29
+ <%- end -%>
11
30
  <%-if method.arguments.any?-%>
12
31
  #
13
32
  <%- arg_list = method.arguments.map { |arg| "#{arg.name}: nil"}.join ", " -%>
@@ -1,6 +1,14 @@
1
1
  <%- assert_locals method -%>
2
+ <%- if method.resumable_upload? -%>
3
+ # @return [::Gapic::ResumableUpload]
4
+ # A reusable upload handle. No request is sent and no byte is read from a stream until
5
+ # {::Gapic::ResumableUpload#start} or {::Gapic::ResumableUpload#resume} is called on it.
6
+ # Upload failures, including a failed initiation request, are raised by those methods,
7
+ # not by this one.
8
+ <%- else -%>
2
9
  # @yield [response, operation] Access the result along with the RPC operation
3
10
  # @yieldparam response [<%= method.doc_response_type %>]
4
11
  # @yieldparam operation [::GRPC::ActiveCall::Operation]
5
12
  #
6
13
  # @return [<%= method.doc_response_type %>]
14
+ <%- end -%>
@@ -21,6 +21,14 @@ require "securerandom"
21
21
  <%= indent service.doc_description(transport: :rest), "# " %>
22
22
  #
23
23
  <%- end -%>
24
+ <%- if service.resumable_upload? -%>
25
+ <%- upload_names = service.resumable_upload_methods.map { |upload_method| "`#{upload_method.name}`" }.join ", " -%>
26
+ <%- upload_many = service.resumable_upload_methods.length > 1 -%>
27
+ # <%= upload_names %> <%= upload_many ? "perform resumable uploads: each returns" : "performs a resumable upload: it returns" %> a {::Gapic::ResumableUpload} handle
28
+ # instead of a response, and the per-call `timeout` and retry policy cover only the request that
29
+ # creates the upload session, not the upload itself.
30
+ #
31
+ <%- end -%>
24
32
  class <%= service.rest.client_name %>
25
33
  # @private
26
34
  API_VERSION = "<%= service.api_version %>".freeze
@@ -141,6 +149,16 @@ class <%= service.rest.client_name %>
141
149
  <%- end -%>
142
150
  end
143
151
 
152
+ <%- end -%>
153
+ <%- if service.resumable_upload? -%>
154
+ <%= service.resumable_upload_stub_ivar %> = <%= service.resumable_upload_stub_name_full %>.new(
155
+ endpoint: @config.endpoint,
156
+ endpoint_template: DEFAULT_ENDPOINT_TEMPLATE,
157
+ universe_domain: @config.universe_domain,
158
+ credentials: credentials,
159
+ logger: @config.logger
160
+ )
161
+
144
162
  <%- end -%>
145
163
  @<%= service.stub_name %> = <%= service.rest.service_stub_name_full %>.new(
146
164
  endpoint: @config.endpoint,
@@ -162,6 +162,14 @@ class Configuration
162
162
  <%- method_service.rest.methods.each do |method| -%>
163
163
  ##
164
164
  # RPC-specific configuration for `<%= method.name %>`
165
+ <%- if method.resumable_upload? -%>
166
+ #
167
+ # `<%= method.name %>` performs a resumable upload, so the `timeout` and `retry_policy`
168
+ # configured here cover the request that creates the upload session only, not the upload
169
+ # itself. The whole-upload budget is the `upload_timeout:` argument of
170
+ # {::Gapic::ResumableUpload#start} and {::Gapic::ResumableUpload#resume}.
171
+ #
172
+ <%- end -%>
165
173
  # @return [::Gapic::Config::Method]
166
174
  #
167
175
  attr_reader :<%= method.name %>
@@ -7,10 +7,12 @@
7
7
  <%= render partial: "service/rest/client/method/docs/request", locals: { method: method } -%>
8
8
  <%= render partial: "service/rest/client/method/docs/result", locals: { method: method } -%>
9
9
  #
10
+ <%- unless method.resumable_upload? -%>
10
11
  <%= render partial: "service/rest/client/method/docs/error", locals: { method: method } -%>
11
12
  #
13
+ <%- end -%>
12
14
  <%= render partial: "service/rest/client/method/docs/snippets", locals: { method: method } -%>
13
- def <%= method.name %> request, options = nil
15
+ def <%= method.name %> request<%= method.resumable_upload? ? " = {}" : "" %>, options = nil
14
16
  <%= indent render(partial: "service/client/method/def/request", locals: { method: method }), 2 %>
15
17
 
16
18
  <%= indent render(partial: "service/rest/client/method/def/options_defaults", locals: { method: method }), 2 %>
@@ -20,5 +22,7 @@ def <%= method.name %> request, options = nil
20
22
  <%- end -%>
21
23
 
22
24
  <%= indent render(partial: "service/rest/client/method/def/response", locals: { method: method }), 2 %>
25
+ <%- unless method.resumable_upload? -%>
23
26
  <%= render partial: "service/rest/client/method/def/rescue", locals: { method: method } -%>
27
+ <%- end -%>
24
28
  end
@@ -1,5 +1,7 @@
1
1
  <%- assert_locals method -%>
2
- <%- if method.rest.paged? -%>
2
+ <%- if method.resumable_upload? -%>
3
+ <%= render partial: "service/rest/client/method/def/response_resumable_upload", locals: { method: method } -%>
4
+ <%- elsif method.rest.paged? -%>
3
5
  <%= render partial: "service/rest/client/method/def/response_paged", locals: { method: method } -%>
4
6
  <%- elsif method.rest.nonstandard_lro? -%>
5
7
  <%= render partial: "service/rest/client/method/def/response_nonstandard_lro", locals: { method: method } -%>
@@ -0,0 +1,13 @@
1
+ <%- assert_locals method -%>
2
+ <%- error_handler = render(partial: "service/rest/client/method/def/upload_error_handler", locals: { method: method }).strip -%>
3
+ ::Gapic::ResumableUpload.new(
4
+ client_stub_proc: -> { <%= method.service.resumable_upload_stub_ivar %>.client_stub },
5
+ initial_request_proc: lambda {
6
+ <%= method.service.resumable_upload_stub_name_full %>.<%= method.rest.transcoding_helper_name %> request
7
+ },
8
+ initial_headers: options.metadata,
9
+ start_retry_policy: ::Gapic::Rest::ResumableUpload.start_retry_policy_for(options),
10
+ response_type: <%= method.return_type %>,
11
+ method_name: "<%= method.name %>",
12
+ error_handler: <%= error_handler %>
13
+ )
@@ -0,0 +1,2 @@
1
+ <%- assert_locals method -%>
2
+ nil
@@ -1,4 +1,22 @@
1
1
  <%- assert_locals method -%>
2
+ <%- if method.resumable_upload? -%>
3
+ # @overload <%= method.name %>(request = {}, options = nil)
4
+ # Pass arguments to `<%= method.name %>` via a request object, either of type
5
+ # {<%= method.request_type %>} or an equivalent Hash.
6
+ #
7
+ # @param request [<%= method.request_type %>, ::Hash]
8
+ # A request object representing the call parameters. Optional: it is ignored when the returned
9
+ # handle is resumed rather than started, because a resumed upload targets a session the server
10
+ # has already created.
11
+ # @param options [::Gapic::CallOptions, ::Hash]
12
+ # Overrides for the initiation request only. The `timeout`, `retry_policy` and `metadata` set
13
+ # here apply to the single request that creates the upload session, not to the upload as a
14
+ # whole: an upload still transferring bytes an hour later has long outlived this `timeout`.
15
+ # To bound the whole upload, pass `upload_timeout:` to {::Gapic::ResumableUpload#start} or
16
+ # {::Gapic::ResumableUpload#resume}. Chunk transfers are retried by the upload protocol itself,
17
+ # with the defaults documented on {::Gapic::ResumableUpload}; no call option reaches them.
18
+ # Optional.
19
+ <%- else -%>
2
20
  # @overload <%= method.name %>(request, options = nil)
3
21
  # Pass arguments to `<%= method.name %>` via a request object, either of type
4
22
  # {<%= method.request_type %>} or an equivalent Hash.
@@ -8,6 +26,7 @@
8
26
  # parameters, or to keep all the default parameter values, pass an empty Hash.
9
27
  # @param options [::Gapic::CallOptions, ::Hash]
10
28
  # Overrides the default settings for this call, e.g, timeout, retries etc. Optional.
29
+ <%- end -%>
11
30
  <%-if method.arguments.any?-%>
12
31
  #
13
32
  <%- arg_list = method.arguments.map { |arg| "#{arg.name}: nil"}.join ", " -%>
@@ -1,5 +1,11 @@
1
1
  <%- assert_locals method -%>
2
- <%- if method.server_streaming? -%>
2
+ <%- if method.resumable_upload? -%>
3
+ # @return [::Gapic::ResumableUpload]
4
+ # A reusable upload handle. No request is sent and no byte is read from a stream until
5
+ # {::Gapic::ResumableUpload#start} or {::Gapic::ResumableUpload#resume} is called on it.
6
+ # Upload failures, including a failed initiation request, are raised by those methods,
7
+ # not by this one.
8
+ <%- elsif method.server_streaming? -%>
3
9
  # @return [::Enumerable<<%= method.rest.doc_response_type %>>]
4
10
  <%- else -%>
5
11
  # @yield [result, operation] Access the result along with the TransportOperation object
@@ -52,11 +52,11 @@ class <%= service.rest.service_stub_name %>
52
52
  stub ? @client_stub.stub_logger : @client_stub.logger
53
53
  end
54
54
 
55
- <%- service.rest.methods.each do |method| -%>
55
+ <%- service.rest.service_stub_methods.each do |method| -%>
56
56
  <%= indent_tail render(partial: "service/rest/service_stub/method/def", locals: { method: method, service_stub_name: service.rest.service_stub_name }), 2 %>
57
57
 
58
58
  <%- end %>
59
- <%- service.rest.methods.each do |method| -%>
59
+ <%- service.rest.service_stub_methods.each do |method| -%>
60
60
  <%= indent_tail render(partial: "service/rest/service_stub/grpc_transcoding_method/def", locals: { method: method }), 2 %>
61
61
 
62
62
  <%- end %>
@@ -0,0 +1,15 @@
1
+ <%- assert_locals method -%>
2
+ <%- method.http_bindings.each do |http_binding| -%>
3
+ .with_bindings(
4
+ uri_method: :<%= http_binding.verb %>,
5
+ uri_template: "<%= http_binding.uri_for_transcoding %>",
6
+ <%- if http_binding.body? -%>
7
+ body: "<%= http_binding.body %>",
8
+ <%- end -%>
9
+ matches: [
10
+ <%- http_binding.routing_params_transcoder_matches_strings.each do |match_str| -%>
11
+ <%= match_str %>
12
+ <%- end -%>
13
+ ]
14
+ )
15
+ <%- end -%>
@@ -19,19 +19,6 @@ def self.<%= method.rest.transcoding_helper_name %> request_pb<%= boverr_str %>
19
19
  <%- end %>
20
20
  <%- assignment = method.service.rest.is_main_mixin_service? ? "||=" : "=" -%>
21
21
  transcoder <%= assignment %> Gapic::Rest::GrpcTranscoder.new
22
- <%- method.http_bindings.each do |http_binding| -%>
23
- .with_bindings(
24
- uri_method: :<%= http_binding.verb %>,
25
- uri_template: "<%= http_binding.uri_for_transcoding %>",
26
- <%- if http_binding.body? -%>
27
- body: "<%= http_binding.body %>",
28
- <%- end -%>
29
- matches: [
30
- <%- http_binding.routing_params_transcoder_matches_strings.each do |match_str| -%>
31
- <%= match_str %>
32
- <%- end -%>
33
- ]
34
- )
35
- <%- end %>
22
+ <%= indent render(partial: "service/rest/service_stub/grpc_transcoding_method/bindings", locals: { method: method }), 44 %>
36
23
  transcoder.transcode request_pb
37
24
  end
@@ -9,7 +9,7 @@ require "<%= service.rest.service_require %>"
9
9
  class <%= service.rest.client_name_full %>Test < Minitest::Test
10
10
  <%= indent render(partial: "service/rest/test/method/setup"), 2 %>
11
11
 
12
- <% service.rest.methods.each do |method| %>
12
+ <% service.rest.methods.reject(&:resumable_upload?).each do |method| %>
13
13
  <%= indent render(partial: "service/rest/test/method/#{method.kind}",
14
14
  locals: { method: method }), 2 %>
15
15
 
@@ -0,0 +1,64 @@
1
+ <%- assert_locals service -%>
2
+ ##
3
+ # Resumable upload stub for the <%= service.name %> service.
4
+ #
5
+ # Owns the REST client stub the uploads are performed through, the transcoders that build their
6
+ # initiation requests, and the credentials error that a client unable to perform REST calls has to
7
+ # raise when an upload is attempted.
8
+ #
9
+ # @private
10
+ #
11
+ class <%= service.resumable_upload_stub_name %>
12
+ <%- service.resumable_upload_methods.each do |method| -%>
13
+ # @private
14
+ <%= method.upload_url_prefix_const_name %> = "<%= method.upload_url_prefix %>"
15
+
16
+ <%- end -%>
17
+ # @private
18
+ def initialize endpoint:, endpoint_template:, universe_domain:, credentials:, logger:
19
+ # These require statements are intentionally placed here to initialize
20
+ # the REST modules only when it's required.
21
+ require "gapic/rest"
22
+
23
+ # Uploads travel over REST, and credentials cannot be recovered from a gRPC channel. Rather than
24
+ # raise while a client is being constructed, which would break every other method on it, the
25
+ # error is stored and raised when an upload is actually attempted.
26
+ if defined?(::GRPC::Core::Channel) &&
27
+ (credentials.is_a?(::GRPC::Core::Channel) || credentials.is_a?(::GRPC::Core::ChannelCredentials))
28
+ @credentials_error = ::ArgumentError.new(
29
+ "Resumable uploads are performed over REST and cannot use a gRPC channel as credentials, " \
30
+ "because credentials cannot be recovered from a channel. Construct the client with Google " \
31
+ "OAuth credentials (a keyfile path, a Hash, or a Google::Auth::Credentials instance) to use " \
32
+ "resumable upload methods."
33
+ )
34
+ return
35
+ end
36
+
37
+ @client_stub = ::Gapic::Rest::ClientStub.new endpoint: endpoint,
38
+ endpoint_template: endpoint_template,
39
+ universe_domain: universe_domain,
40
+ credentials: credentials,
41
+ numeric_enums: false,
42
+ service_name: self.class,
43
+ raise_faraday_errors: false,
44
+ logger: logger
45
+ end
46
+
47
+ ##
48
+ # @private
49
+ #
50
+ # The REST client stub the uploads are performed through.
51
+ #
52
+ # @return [::Gapic::Rest::ClientStub]
53
+ #
54
+ # @raise [::ArgumentError] if this client cannot perform REST calls.
55
+ #
56
+ def client_stub
57
+ raise @credentials_error if @credentials_error
58
+ @client_stub
59
+ end
60
+ <%- service.resumable_upload_methods.each do |method| -%>
61
+
62
+ <%= indent_tail render(partial: "service/resumable_upload_stub/transcoding_method", locals: { method: method, service: service }), 2 %>
63
+ <%- end -%>
64
+ end
@@ -0,0 +1,22 @@
1
+ <%- assert_locals method, service -%>
2
+ ##
3
+ # @private
4
+ #
5
+ # GRPC transcoding helper method for the <%= method.name %> resumable upload initiation request.
6
+ #
7
+ # The upload itself is not transcoded: only the request that creates the upload session is, and the
8
+ # verb is discarded because initiation is always a POST. Query string parameters are folded into the
9
+ # URL, since the upload sends the initiation request with no separate parameters.
10
+ #
11
+ # @param request_pb [<%= method.request_type %>]
12
+ # A request object representing the call parameters. Required.
13
+ # @return [Array(String, [String, nil])]
14
+ # Uri, Body
15
+ def self.<%= method.rest.transcoding_helper_name %> request_pb
16
+ transcoder = ::Gapic::Rest::GrpcTranscoder.new
17
+ <%= indent render(partial: "service/rest/service_stub/grpc_transcoding_method/bindings", locals: { method: method }), 46 %>
18
+ _verb, uri, query_string_params, body = transcoder.transcode request_pb
19
+ uri = "/#{<%= method.upload_url_prefix_const_name %>}#{uri}"
20
+ uri = "#{uri}?#{query_string_params.join '&'}" if query_string_params.any?
21
+ [uri, body]
22
+ end
@@ -0,0 +1,6 @@
1
+ <%- assert_locals service -%>
2
+ <%= render partial: "service/resumable_upload_stub/resumable_upload_stub",
3
+ layout: "layouts/ruby",
4
+ locals: { service: service,
5
+ namespace: service.service_name_full }
6
+ %>
@@ -10,7 +10,7 @@ require "<%= service.service_require %>"
10
10
  class <%= service.client_name_full %>Test < Minitest::Test
11
11
  <%= indent render(partial: "service/test/method/setup"), 2 %>
12
12
 
13
- <% service.methods.each do |method| %>
13
+ <% service.methods.reject(&:resumable_upload?).each do |method| %>
14
14
  <%= indent render(partial: "service/test/method/#{method.kind}",
15
15
  locals: { method: method }), 2 %>
16
16
 
@@ -0,0 +1,132 @@
1
+ <%- assert_locals service -%>
2
+ <%= render partial: "shared/header" %>
3
+ require "helper"
4
+
5
+ require "gapic/rest"
6
+ <%- if service.generate_grpc_clients? -%>
7
+ require "gapic/grpc/service_stub"
8
+ <%- end -%>
9
+
10
+ require "<%= service.proto_service_require %>"
11
+ <%- if service.generate_grpc_clients? -%>
12
+ require "<%= service.service_require %>"
13
+ <%- end -%>
14
+ <%- if service.generate_rest_clients? -%>
15
+ require "<%= service.rest.service_require %>"
16
+ <%- end -%>
17
+
18
+ class <%= service.service_name_full %>::ResumableUploadTest < Minitest::Test
19
+ ##
20
+ # Stands in for the transport stub a client builds in its constructor. An upload method sends
21
+ # nothing, so this only has to satisfy the constructor.
22
+ #
23
+ class ClientStub
24
+ def endpoint
25
+ "endpoint.example.com"
26
+ end
27
+
28
+ def universe_domain
29
+ "example.com"
30
+ end
31
+
32
+ def stub_logger
33
+ nil
34
+ end
35
+
36
+ def logger
37
+ nil
38
+ end
39
+ end
40
+ <%- service.resumable_upload_methods.each do |method| -%>
41
+ <%- fields = method.fields_with_first_oneof -%>
42
+ <%- prefix_const = "#{service.resumable_upload_stub_name_full}::#{method.upload_url_prefix_const_name}" -%>
43
+
44
+ def test_transcode_<%= method.name %>_request
45
+ request = <%= method.request_type %>.new
46
+
47
+ # The transcoder is stubbed so the test pins the upload-specific post-processing (prefix,
48
+ # query-string folding, [uri, body] shape) independently of the method's path template.
49
+ transcoder = Gapic::Rest::GrpcTranscoder.new
50
+ transcoder.stub :transcode, [:post, "/transcoded/path", ["foo=bar", "baz=qux"], "{\"body\":true}"] do
51
+ Gapic::Rest::GrpcTranscoder.stub :new, transcoder do
52
+ uri, body = <%= service.resumable_upload_stub_name_full %>.<%= method.rest.transcoding_helper_name %> request
53
+
54
+ assert_equal "/#{<%= prefix_const %>}/transcoded/path?foo=bar&baz=qux", uri
55
+ assert_equal "{\"body\":true}", body
56
+ end
57
+ end
58
+ end
59
+ <%- if service.generate_grpc_clients? -%>
60
+
61
+ def test_<%= method.name %>
62
+ <%- fields.each do |field| -%>
63
+ <%= field.value_name %> = <%= field.default_value %>
64
+ <%- end -%>
65
+
66
+ client = nil
67
+ Gapic::ServiceStub.stub :new, ClientStub.new do
68
+ client = <%= service.client_name_full %>.new do |config|
69
+ config.credentials = :this_channel_is_insecure
70
+ end
71
+ end
72
+
73
+ upload = client.<%= method.name %> <%= method.request_type %>.new(<%= fields.map(&:as_kwarg).join ", " %>)
74
+
75
+ # The call builds a handle and sends nothing. The upload only begins on #start or #resume.
76
+ assert_kind_of ::Gapic::ResumableUpload, upload
77
+ assert_nil upload.resume_handle
78
+ refute upload.resumable?
79
+ refute upload.running?
80
+ end
81
+
82
+ def test_<%= method.name %>_channel_credentials
83
+ <%- fields.each do |field| -%>
84
+ <%= field.value_name %> = <%= field.default_value %>
85
+ <%- end -%>
86
+ grpc_channel = GRPC::Core::Channel.new "localhost:8888", nil, :this_channel_is_insecure
87
+
88
+ client = nil
89
+ Gapic::ServiceStub.stub :new, ClientStub.new do
90
+ client = <%= service.client_name_full %>.new do |config|
91
+ config.credentials = grpc_channel
92
+ end
93
+ end
94
+
95
+ upload = client.<%= method.name %> <%= method.request_type %>.new(<%= fields.map(&:as_kwarg).join ", " %>)
96
+
97
+ assert_kind_of ::Gapic::ResumableUpload, upload
98
+
99
+ # Uploads travel over REST and cannot use a channel as credentials. The error surfaces here,
100
+ # rather than at construction, and before the stream is touched.
101
+ stream = StringIO.new "resumable upload test payload"
102
+ assert_raises ::ArgumentError do
103
+ upload.start stream: stream, content_type: "application/octet-stream"
104
+ end
105
+ assert_equal 0, stream.pos
106
+ end
107
+ <%- end -%>
108
+ <%- if service.generate_rest_clients? -%>
109
+
110
+ def test_<%= method.name %>_rest
111
+ <%- fields.each do |field| -%>
112
+ <%= field.value_name %> = <%= field.default_value %>
113
+ <%- end -%>
114
+
115
+ client = nil
116
+ Gapic::Rest::ClientStub.stub :new, ClientStub.new do
117
+ client = <%= service.rest.client_name_full %>.new do |config|
118
+ config.credentials = :dummy_value
119
+ end
120
+ end
121
+
122
+ upload = client.<%= method.name %> <%= method.request_type %>.new(<%= fields.map(&:as_kwarg).join ", " %>)
123
+
124
+ # The call builds a handle and sends nothing. The upload only begins on #start or #resume.
125
+ assert_kind_of ::Gapic::ResumableUpload, upload
126
+ assert_nil upload.resume_handle
127
+ refute upload.resumable?
128
+ refute upload.running?
129
+ end
130
+ <%- end -%>
131
+ <%- end -%>
132
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gapic-generator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.52.0
4
+ version: 0.53.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ernest Landrito
@@ -150,6 +150,7 @@ files:
150
150
  - lib/gapic/model/api_metadata.rb
151
151
  - lib/gapic/model/method/http_annotation.rb
152
152
  - lib/gapic/model/method/lro.rb
153
+ - lib/gapic/model/method/resumable_upload.rb
153
154
  - lib/gapic/model/method/routing.rb
154
155
  - lib/gapic/model/mixins.rb
155
156
  - lib/gapic/model/model_error.rb
@@ -267,7 +268,9 @@ files:
267
268
  - templates/default/service/client/method/def/_response_normal.text.erb
268
269
  - templates/default/service/client/method/def/_response_normal_lro.text.erb
269
270
  - templates/default/service/client/method/def/_response_paged.text.erb
271
+ - templates/default/service/client/method/def/_response_resumable_upload.text.erb
270
272
  - templates/default/service/client/method/def/_routing_params.text.erb
273
+ - templates/default/service/client/method/def/_upload_error_handler.text.erb
271
274
  - templates/default/service/client/method/docs/_error.text.erb
272
275
  - templates/default/service/client/method/docs/_request.text.erb
273
276
  - templates/default/service/client/method/docs/_request_field.text.erb
@@ -306,7 +309,9 @@ files:
306
309
  - templates/default/service/rest/client/method/def/_response_normal.text.erb
307
310
  - templates/default/service/rest/client/method/def/_response_normal_lro.text.erb
308
311
  - templates/default/service/rest/client/method/def/_response_paged.text.erb
312
+ - templates/default/service/rest/client/method/def/_response_resumable_upload.text.erb
309
313
  - templates/default/service/rest/client/method/def/_response_server_streaming.text.erb
314
+ - templates/default/service/rest/client/method/def/_upload_error_handler.text.erb
310
315
  - templates/default/service/rest/client/method/docs/_error.text.erb
311
316
  - templates/default/service/rest/client/method/docs/_request.text.erb
312
317
  - templates/default/service/rest/client/method/docs/_result.text.erb
@@ -316,6 +321,7 @@ files:
316
321
  - templates/default/service/rest/operations.text.erb
317
322
  - templates/default/service/rest/service_stub.text.erb
318
323
  - templates/default/service/rest/service_stub/_service_stub.text.erb
324
+ - templates/default/service/rest/service_stub/grpc_transcoding_method/_bindings.text.erb
319
325
  - templates/default/service/rest/service_stub/grpc_transcoding_method/_def.text.erb
320
326
  - templates/default/service/rest/service_stub/grpc_transcoding_method/def/_query_string_param.text.erb
321
327
  - templates/default/service/rest/service_stub/method/_def.text.erb
@@ -327,6 +333,9 @@ files:
327
333
  - templates/default/service/rest/test/method/_normal.text.erb
328
334
  - templates/default/service/rest/test/method/_server.text.erb
329
335
  - templates/default/service/rest/test/method/_setup.text.erb
336
+ - templates/default/service/resumable_upload_stub.text.erb
337
+ - templates/default/service/resumable_upload_stub/_resumable_upload_stub.text.erb
338
+ - templates/default/service/resumable_upload_stub/_transcoding_method.text.erb
330
339
  - templates/default/service/test/_resource.text.erb
331
340
  - templates/default/service/test/client.text.erb
332
341
  - templates/default/service/test/client_operations.text.erb
@@ -338,6 +347,7 @@ files:
338
347
  - templates/default/service/test/method/_normal.text.erb
339
348
  - templates/default/service/test/method/_server.text.erb
340
349
  - templates/default/service/test/method/_setup.text.erb
350
+ - templates/default/service/test/resumable_upload.text.erb
341
351
  - templates/default/service/test/smoke.text.erb
342
352
  - templates/default/shared/_header.text.erb
343
353
  - templates/default/shared/_license.text.erb