gapic-generator 0.51.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/lib/gapic/formatting_utils.rb +85 -18
- data/lib/gapic/generator/version.rb +1 -1
- data/lib/gapic/generators/default_generator.rb +6 -0
- data/lib/gapic/generators/default_generator_parameters.rb +2 -0
- data/lib/gapic/model/method/resumable_upload.rb +143 -0
- data/lib/gapic/presenters/gem_presenter.rb +1 -1
- data/lib/gapic/presenters/method_presenter.rb +35 -0
- data/lib/gapic/presenters/service_presenter.rb +65 -0
- data/lib/gapic/presenters/service_rest_presenter.rb +12 -0
- data/lib/gapic/presenters/snippet/response_handling_presenters.rb +60 -0
- data/lib/gapic/presenters/snippet_presenter.rb +16 -6
- data/templates/default/lib/_service.text.erb +3 -0
- data/templates/default/lib/rest/_rest.text.erb +3 -0
- data/templates/default/service/client/_client.text.erb +18 -0
- data/templates/default/service/client/_config.text.erb +8 -0
- data/templates/default/service/client/method/_def.text.erb +5 -1
- data/templates/default/service/client/method/def/_options_defaults.text.erb +5 -0
- data/templates/default/service/client/method/def/_response.text.erb +3 -1
- data/templates/default/service/client/method/def/_response_resumable_upload.text.erb +13 -0
- data/templates/default/service/client/method/def/_upload_error_handler.text.erb +2 -0
- data/templates/default/service/client/method/docs/_request_normal.text.erb +19 -0
- data/templates/default/service/client/method/docs/_response.text.erb +8 -0
- data/templates/default/service/rest/client/_client.text.erb +18 -0
- data/templates/default/service/rest/client/_config.text.erb +8 -0
- data/templates/default/service/rest/client/method/_def.text.erb +5 -1
- data/templates/default/service/rest/client/method/def/_response.text.erb +3 -1
- data/templates/default/service/rest/client/method/def/_response_resumable_upload.text.erb +13 -0
- data/templates/default/service/rest/client/method/def/_upload_error_handler.text.erb +2 -0
- data/templates/default/service/rest/client/method/docs/_request.text.erb +19 -0
- data/templates/default/service/rest/client/method/docs/_result.text.erb +7 -1
- data/templates/default/service/rest/service_stub/_service_stub.text.erb +2 -2
- data/templates/default/service/rest/service_stub/grpc_transcoding_method/_bindings.text.erb +15 -0
- data/templates/default/service/rest/service_stub/grpc_transcoding_method/_def.text.erb +1 -14
- data/templates/default/service/rest/test/client.text.erb +1 -1
- data/templates/default/service/resumable_upload_stub/_resumable_upload_stub.text.erb +64 -0
- data/templates/default/service/resumable_upload_stub/_transcoding_method.text.erb +22 -0
- data/templates/default/service/resumable_upload_stub.text.erb +6 -0
- data/templates/default/service/test/client.text.erb +1 -1
- data/templates/default/service/test/resumable_upload.text.erb +132 -0
- metadata +11 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3264cfe286069bf2e3bed64ae2246c0b8e48d97851809c99c5520e072fa38889
|
|
4
|
+
data.tar.gz: d9503be57f76aedd856a46159e138673fc83f2912fa51018d16e5cd1e8c71f90
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d8109c1036d6b95c2bf2fe201de7e2cf26516e4d315c6d4ca3331b52f0d312d27884416bd6ce7db8209ae78c13ffab4120b444e229fc914d3d329ca801dd6837
|
|
7
|
+
data.tar.gz: 1ef6ae68e1fc76dfc19bbe947d7f4340918403494d57bd3d1ba40540b953921470a5090b7ed7f6d2fe80519066c28d92e8a0a3115acbf4d498fc43c8580debd9
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
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
|
+
|
|
8
|
+
### 0.52.0 / 2026-09-04
|
|
9
|
+
|
|
10
|
+
* Fix: escape multi-line braces and backtick unknown doc tags in yard formatting
|
|
11
|
+
* Fix: rejoin split doc URLs (b/153077040) and strip non-existent message links (b/158466893)
|
|
12
|
+
* Feature: add ruby-cloud-renamed-from for renamed wrapper gems
|
|
13
|
+
|
|
3
14
|
### 0.51.1 / 2026-08-05
|
|
4
15
|
|
|
5
16
|
No significant changes
|
|
@@ -21,16 +21,30 @@ module Gapic
|
|
|
21
21
|
# Various string formatting utils
|
|
22
22
|
#
|
|
23
23
|
module FormattingUtils
|
|
24
|
-
@brace_detector = /\A(?<pre>[^`]*(?:`[^`]*`[^`]*)*[^`\\])?\{(?<inside>[^\s][^}]*)\}(?<post>.*)\z/m
|
|
25
24
|
@xref_detector = /\A(?<pre>[^`]*(?:`[^`]*`[^`]*)*)?\[(?<text>[\w. `-]+)\]\[(?<addr>[\w.]+)\](?<post>.*)\z/m
|
|
26
25
|
@list_element_detector = /\A\s*(?:\*|\+|-|[0-9a-zA-Z]+\.)\s/
|
|
27
26
|
@omit_lines = ["@InputOnly\n", "@OutputOnly\n"]
|
|
27
|
+
# Built-in YARD meta-data tags as documented in:
|
|
28
|
+
# https://rubydoc.info/gems/yard/file/docs/Tags.md#Tag_List
|
|
29
|
+
@known_yard_tags = [
|
|
30
|
+
"abstract", "api", "attr", "attr_reader", "attr_writer", "author", "deprecated", "example",
|
|
31
|
+
"note", "option", "overload", "param", "private", "raise", "return", "see", "since", "todo",
|
|
32
|
+
"version", "yield", "yieldparam", "yieldreturn"
|
|
33
|
+
].freeze
|
|
34
|
+
|
|
35
|
+
# Non-existent messages referenced in proto documentation comments (b/158466893).
|
|
36
|
+
# Cross-references to these messages (or their fields) cannot be resolved, so the links
|
|
37
|
+
# are stripped to avoid broken documentation references.
|
|
38
|
+
@non_existent_messages = [
|
|
39
|
+
"google.cloud.automl.v1.ColumnSpec"
|
|
40
|
+
].freeze
|
|
28
41
|
|
|
29
42
|
class << self
|
|
30
43
|
##
|
|
31
44
|
# Given an enumerable of lines, performs yardoc formatting, including:
|
|
32
45
|
# * Interpreting cross-references identified as described in AIP 192
|
|
33
46
|
# * Escaping literal braces that look like yardoc type links
|
|
47
|
+
# * Backticking unknown doc tags so they are not parsed as YARD tags
|
|
34
48
|
#
|
|
35
49
|
# Tries to be smart about exempting preformatted text blocks.
|
|
36
50
|
#
|
|
@@ -45,23 +59,26 @@ module Gapic
|
|
|
45
59
|
#
|
|
46
60
|
def format_doc_lines api, lines, disable_xrefs: false, transport: nil
|
|
47
61
|
transport ||= api&.default_transport || :grpc
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# previous block, which is normally 0 except if we're in a list item.
|
|
51
|
-
# Then, if a block is indented at least 4 spaces past that expected
|
|
52
|
-
# indent (and as long as it remains so), those lines are considered
|
|
53
|
-
# preformatted.
|
|
62
|
+
lines = rejoin_split_urls lines
|
|
63
|
+
in_fence = in_code_span = false
|
|
54
64
|
in_block = nil
|
|
55
65
|
base_indent = 0
|
|
56
66
|
(lines - @omit_lines).map do |line|
|
|
57
|
-
|
|
58
|
-
|
|
67
|
+
if line =~ /^\s*(?:```|~~~)/
|
|
68
|
+
in_fence = !in_fence
|
|
69
|
+
in_code_span = false
|
|
59
70
|
in_block = nil
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if
|
|
63
|
-
|
|
64
|
-
|
|
71
|
+
elsif !in_fence
|
|
72
|
+
indent = line_indent line
|
|
73
|
+
if indent.nil?
|
|
74
|
+
in_block = nil
|
|
75
|
+
in_code_span = false
|
|
76
|
+
else
|
|
77
|
+
in_block, base_indent = update_indent_state in_block, base_indent, line, indent
|
|
78
|
+
if in_block == false
|
|
79
|
+
line, in_code_span = format_line_content line, in_code_span
|
|
80
|
+
line = format_line_xrefs api, line, disable_xrefs, transport
|
|
81
|
+
end
|
|
65
82
|
end
|
|
66
83
|
end
|
|
67
84
|
line
|
|
@@ -88,6 +105,21 @@ module Gapic
|
|
|
88
105
|
|
|
89
106
|
private
|
|
90
107
|
|
|
108
|
+
def rejoin_split_urls lines
|
|
109
|
+
return lines if lines.empty?
|
|
110
|
+
|
|
111
|
+
# Fix for misformatted markdown links across line breaks (b/153077040).
|
|
112
|
+
# Callers may pass lines with trailing newlines (e.g., from String#each_line in schema wrappers)
|
|
113
|
+
# or without trailing newlines (e.g., from String#split("\n") in GemPresenter#readme_description).
|
|
114
|
+
# We must preserve the presence or absence of trailing newlines on each element.
|
|
115
|
+
has_newlines = lines.any? { |l| l.end_with? "\n" }
|
|
116
|
+
if has_newlines
|
|
117
|
+
lines.join.gsub(%r{https:\n\s*//}, "https://").each_line.to_a
|
|
118
|
+
else
|
|
119
|
+
lines.join("\n").gsub(%r{https:\n\s*//}, "https://").split("\n", -1)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
91
123
|
def update_indent_state in_block, base_indent, line, indent
|
|
92
124
|
if in_block != true && @list_element_detector =~ line
|
|
93
125
|
in_block = false
|
|
@@ -106,15 +138,50 @@ module Gapic
|
|
|
106
138
|
m[1].length
|
|
107
139
|
end
|
|
108
140
|
|
|
109
|
-
def
|
|
110
|
-
|
|
111
|
-
|
|
141
|
+
def format_line_content line, in_code_span
|
|
142
|
+
parts = line.split("`", -1)
|
|
143
|
+
formatted_parts = parts.each_with_index.map do |part, idx|
|
|
144
|
+
if in_code_span
|
|
145
|
+
in_code_span = false if idx < parts.length - 1
|
|
146
|
+
part
|
|
147
|
+
else
|
|
148
|
+
is_followed_by_backtick = idx < parts.length - 1
|
|
149
|
+
in_code_span = true if is_followed_by_backtick
|
|
150
|
+
formatted = escape_prose_braces part, is_followed_by_backtick: is_followed_by_backtick
|
|
151
|
+
sanitize_prose_tags formatted
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
[formatted_parts.join("`"), in_code_span]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def escape_prose_braces text, is_followed_by_backtick: false
|
|
158
|
+
# Matches unescaped `{` outside backtick spans followed by non-whitespace.
|
|
159
|
+
# If `{` is at the end of a non-code chunk (is_followed_by_backtick: true), it is followed
|
|
160
|
+
# immediately by a backticked code span (starting with a non-whitespace backtick),
|
|
161
|
+
# so \z (end of string) is also matched.
|
|
162
|
+
pattern = is_followed_by_backtick ? /(?<!\\)\{(?=[^\s]|\z)/ : /(?<!\\)\{(?=[^\s])/
|
|
163
|
+
text.gsub(pattern) { "\\\\{" }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def sanitize_prose_tags text
|
|
167
|
+
# Matches doc tags starting with `@` at the start of a line or preceded by whitespace.
|
|
168
|
+
# Avoids matching `@` within email addresses (e.g. user@example.com) or quotes.
|
|
169
|
+
# Any tag not in the YARD recognized list (or starting with `!`) is wrapped in backticks
|
|
170
|
+
# so YARD renders it as literal text rather than an unrecognized tag directive.
|
|
171
|
+
text.gsub(/(?<=\A|\s)@([a-zA-Z_]\w*)/) do |match|
|
|
172
|
+
tag = Regexp.last_match 1
|
|
173
|
+
@known_yard_tags.include?(tag) || tag.start_with?("!") ? match : "`#{match}`"
|
|
112
174
|
end
|
|
113
|
-
line
|
|
114
175
|
end
|
|
115
176
|
|
|
116
177
|
def format_line_xrefs api, line, disable_xrefs, transport
|
|
117
178
|
while (m = @xref_detector.match line)
|
|
179
|
+
# Remove links to known non-existent messages (b/158466893)
|
|
180
|
+
if @non_existent_messages.any? { |msg| m[:addr] == msg || m[:addr].start_with?("#{msg}.") }
|
|
181
|
+
line = "#{m[:pre]}#{m[:text]}#{m[:post]}"
|
|
182
|
+
next
|
|
183
|
+
end
|
|
184
|
+
|
|
118
185
|
entity = api.lookup m[:addr]
|
|
119
186
|
is_mixin_field_addr = Gapic::Model::Mixins.mixin_message_field_address?(
|
|
120
187
|
m[:addr],
|
|
@@ -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
|
|
|
@@ -40,6 +40,7 @@ module Gapic
|
|
|
40
40
|
":gem.:homepage",
|
|
41
41
|
":gem.:env_prefix",
|
|
42
42
|
":gem.:version_dependencies",
|
|
43
|
+
":gem.:renamed_from",
|
|
43
44
|
":gem.:migration_version",
|
|
44
45
|
":gem.:product_documentation_url",
|
|
45
46
|
":gem.:issue_tracker_url",
|
|
@@ -83,6 +84,7 @@ module Gapic
|
|
|
83
84
|
"gem-homepage" => ":gem.:homepage",
|
|
84
85
|
"gem-env-prefix" => ":gem.:env_prefix",
|
|
85
86
|
"gem-wrapper-of" => ":gem.:version_dependencies",
|
|
87
|
+
"gem-renamed-from" => ":gem.:renamed_from",
|
|
86
88
|
"gem-migration-version" => ":gem.:migration_version",
|
|
87
89
|
"gem-product-url" => ":gem.:product_documentation_url",
|
|
88
90
|
"gem-issues-url" => ":gem.:issue_tracker_url",
|
|
@@ -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.
|
|
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
|
-
|
|
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 -%>
|