gapic-generator 0.45.2 → 0.45.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f2a5263485263873c059ce342f1cd26c800bab3349b7b191132b57f79d986ea
4
- data.tar.gz: eda4ecd48fe183ffd4f98927f80c6500fc7b1d578fd8a45f4d539c5e0cecc9bd
3
+ metadata.gz: 271aec2d05b52adb5498c05c65461142789397a22d3b9a99d1d09f1559e8cf4f
4
+ data.tar.gz: 16e8bbbf90f2c0eb2174939cc7972fa37552cb807cfd24ecc0d74c9ed252a642
5
5
  SHA512:
6
- metadata.gz: a49ad3a37ed0d730ef3bed251a2d58f06a4f9686d9fb4d65bcc163e1074ff76d427396798e3e9223a9b2d0d4d82be27c712f9c079f3379f573fe7026bfebbd29
7
- data.tar.gz: 84dd1fd882a77ff2c659d3d17e56722bb0f80240dc8a644c282290c321a090352ff315c415d4f0e74172830560868f143b9e48fe0b319ae8a56a36680b0f5089
6
+ metadata.gz: e46aeb79a605c9395704b0cc14d4b8b540c34c119c9ee7d33178fc8d1a384d91fecfc42e92311e7197e36f9cec7cf21cb85e3a29850256643d348eae6571e8d2
7
+ data.tar.gz: 9f953a7d82a33f19a6309b6ca6abdf5da4b0f5bec294f6f1da8b0aadec47466f335a4df091c8021dd87a9596b0a310bdfa006fd617f03af8e167067a76712d47
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Release History for gapic-generator
2
2
 
3
+ ### 0.45.3 / 2025-06-21
4
+
5
+ * Fix: correct pagination heuristic for Compute
6
+ * Fix: add libyaml to checks and configure for the prebuilt binary
7
+ * Fix: update Ruby prebuilt binary (version 3.4.3)
8
+
3
9
  ### 0.45.2 / 2025-06-04
4
10
 
5
11
  * Fix: update Ruby prebuilt binary, version 3.4.3
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Gapic
18
18
  module Generator
19
- VERSION = "0.45.2"
19
+ VERSION = "0.45.3"
20
20
  end
21
21
  end
@@ -160,7 +160,7 @@ module Gapic
160
160
  selector = setting[SELECTOR]
161
161
  methods = service_config.apis.filter_map do |api|
162
162
  # Removes API prefix and trailing period.
163
- selector[api.name.length + 1..].downcase if selector.start_with? api.name
163
+ selector[(api.name.length + 1)..].downcase if selector.start_with? api.name
164
164
  end
165
165
  if !methods.nil? && setting.key?(AUTO_POPULATED_FIELDS)
166
166
  @auto_populated_fields_by_method_name[methods.first] = setting[AUTO_POPULATED_FIELDS]
@@ -14,20 +14,80 @@
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
+ require "gapic/model/model_error"
18
+
17
19
  module Gapic
18
20
  module Presenters
19
21
  module Method
20
22
  ##
21
- # Pagination info determined from the proto method
23
+ # Compute-specific pagination info determined from the proto method
24
+ #
25
+ # This implements the Compute-specific pagination heuristic
26
+ #
27
+ # The following steps are followed for this heuristic:
28
+ # 1. The method should not be server-streamed
29
+ # 2. The request should have a page token and page size fields
30
+ # 3. The response should have a next page token and contain a valid results field
31
+ #
32
+ # Now determining the valid results field is its own complicated sub-heuristic that should be run last.
33
+ # This sub-heuristic cannot end in "not paginated". It should either determine the results field or throw an error
34
+ #
35
+ # The following steps are followed for this sub-heuristic:
36
+ # 0. Check the exception dictionary. If the method is there as a key, use the value as the results field.
37
+ # 1. If there is exactly one map field, that field is the results field.
38
+ #
39
+ # NB: at this point the response contains either 0 or 2+ map fields
40
+ #
41
+ # 2. If there are no repeated fields there is no results field and we shall throw an error
42
+ # 3. If there is exactly one repeated field, that field is the results field.
43
+ # 4. If there are exactly 2 repeated fields, one is of message type, and the other is of type
44
+ # "String", the field of message type is the results field.
45
+ #
46
+ # At this point there are either 2 repeated fields in wrong configuration, or 3 or more repeated
47
+ # fields. The method should have been in the exception dictionary (see step 0).
48
+ # Since the method is NOT in the exception dictionary we should throw an error to prevent
49
+ # accidentally releasing a Compute library with incorrect pagination.
22
50
  #
23
51
  class ComputePaginationInfo
24
52
  include Gapic::Helpers::NamespaceHelper
53
+
54
+ # @private Field name for Pagination Request page token
55
+ PAGE_TOKEN_NAME = "page_token"
56
+ private_constant :PAGE_TOKEN_NAME
57
+ # @private Field type for Pagination Request page token
58
+ PAGE_TOKEN_TYPE = :TYPE_STRING
59
+ private_constant :PAGE_TOKEN_TYPE
60
+
61
+ # @private Field names for Pagination Request page size
62
+ PAGE_SIZE_NAMES = ["page_size", "max_results"].freeze
63
+ private_constant :PAGE_SIZE_NAMES
64
+ # @private Field types for Pagination Request page size
65
+ PAGE_SIZE_TYPES = [:TYPE_UINT32, :TYPE_INT32].freeze
66
+ private_constant :PAGE_SIZE_TYPES
67
+
68
+ # @private Field name for Pagination Response next page token
69
+ NEXT_PAGE_TOKEN_NAME = "next_page_token"
70
+ private_constant :NEXT_PAGE_TOKEN_NAME
71
+ # @private Field type for Pagination Response next page token
72
+ NEXT_PAGE_TOKEN_TYPE = :TYPE_STRING
73
+ private_constant :NEXT_PAGE_TOKEN_TYPE
74
+
75
+ # @private A dictionary of special response messages of paginated methods and their repeated fields
76
+ # Curently contains only UsableSubnetworksAggregatedList which is a paginated field with 3 repeated fields,
77
+ # 2 of which are message-typed fields
78
+ REPEATED_FIELD_SPECIAL_DICTIONARY = {
79
+ "google.cloud.compute.v1.UsableSubnetworksAggregatedList" => "items",
80
+ "google.cloud.compute.v1beta.UsableSubnetworksAggregatedList" => "items"
81
+ }.freeze
82
+ private_constant :REPEATED_FIELD_SPECIAL_DICTIONARY
83
+
25
84
  ##
26
85
  # @param proto_method [Gapic::Schema::Method] the method to derive pagination info from
27
86
  # @param api [Gapic::Schema::Api]
28
87
  #
29
88
  def initialize proto_method, api
30
89
  @api = api
90
+ @method_full_name = proto_method.full_name
31
91
  @request = proto_method.input
32
92
  @response = proto_method.output
33
93
  @server_streaming = proto_method.server_streaming
@@ -38,7 +98,10 @@ module Gapic
38
98
  #
39
99
  # @return [Boolean]
40
100
  def paged?
41
- !server_streaming? && paged_request? && paged_response?
101
+ paged_candidate = !server_streaming? && paged_request? && paged_response_candidate?
102
+
103
+ # `paged_response?` can raise and should be evaluated last
104
+ paged_candidate && paged_response?
42
105
  end
43
106
 
44
107
  ##
@@ -111,7 +174,7 @@ module Gapic
111
174
  def request_page_token_field
112
175
  # Has a String page_token field which specifies the actual (next) page to retrieve.
113
176
  @request_page_token_field ||= @request.fields.find do |f|
114
- f.name == "page_token" && f.type == :TYPE_STRING
177
+ f.name == PAGE_TOKEN_NAME && f.type == PAGE_TOKEN_TYPE
115
178
  end
116
179
  end
117
180
 
@@ -123,14 +186,8 @@ module Gapic
123
186
  def request_page_size_field
124
187
  @request_page_size_field ||=
125
188
  begin
126
- page_size_names = ["page_size", "max_results"]
127
-
128
- # Has the int32 page_size or int32 max_results field
129
- # which defines the maximum number of paginated resources to return in the response.
130
- page_size_types = [:TYPE_UINT32, :TYPE_INT32]
131
-
132
189
  field = @request.fields.find do |f|
133
- page_size_names.include?(f.name) && page_size_types.include?(f.type)
190
+ PAGE_SIZE_NAMES.include?(f.name) && PAGE_SIZE_TYPES.include?(f.type)
134
191
  end
135
192
 
136
193
  field
@@ -143,9 +200,23 @@ module Gapic
143
200
  #
144
201
  # @return [Boolean]
145
202
  def paged_response?
146
- # Has the string next_page_token field to be used in the next request as page_token to retrieve the next page.
147
- # Has only one repeated or map<string, ?> field containing a list of paginated resources.
148
- !response_next_page_token_field.nil? && !response_results_field.nil?
203
+ # Has the string next_page_token field to be used in the next request as
204
+ # page_token to retrieve the next page.
205
+ # Passes the heuristic for paginated response
206
+ # Order is important here, since paginated response heuristic can raise and should be evaluated last
207
+ paged_response_candidate? && !response_results_field.nil?
208
+ end
209
+
210
+ ##
211
+ # Whether the response message for the REGAPIC rpc satisfies the criteria
212
+ # to be a candidate for pagination. This is intentinally split from evaluating
213
+ # the paged response heuristic since that heuristic can raise.
214
+ #
215
+ # @return [Boolean]
216
+ def paged_response_candidate?
217
+ # Has the string next_page_token field to be used in the next request as
218
+ # page_token to retrieve the next page.
219
+ !response_next_page_token_field.nil?
149
220
  end
150
221
 
151
222
  ##
@@ -153,38 +224,102 @@ module Gapic
153
224
  #
154
225
  # @return [Gapic::Schema::Field, nil]
155
226
  def response_next_page_token_field
156
- # Has the string next_page_token field to be used in the next request as page_token to retrieve the next page.
227
+ # Has the string next_page_token field to be used in the next request as
228
+ # page_token to retrieve the next page.
157
229
  @response_next_page_token_field ||= @response.fields.find do |f|
158
- f.name == "next_page_token" && f.type == :TYPE_STRING
230
+ f.name == NEXT_PAGE_TOKEN_NAME && f.type == NEXT_PAGE_TOKEN_TYPE
159
231
  end
160
232
  end
161
233
 
234
+
235
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
236
+ # The heuristic in `response_results_field` would be more confusing if spread across several methods
237
+
162
238
  ##
163
239
  # The field in the response that holds the results
164
- # For Regapic can be either a vanilla repeated field or a map
240
+ # For Compute Regapic can be either a vanilla repeated field or a map
165
241
  #
166
242
  # @return [Gapic::Schema::Field, nil]
167
243
  def response_results_field
244
+ # This sub-heuristic cannot end in "not paginated".
245
+ # It should either determine the results field or throw an error.
168
246
  @response_results_field ||= begin
169
247
  map_fields = @response.fields.find_all(&:map?)
170
248
  repeated_fields = @response.fields.find_all do |f|
171
249
  !f.map? && f.label == :LABEL_REPEATED
172
250
  end
173
251
 
174
- if map_fields.count == 1
175
- # If the response message has only one map<string, ?> field
176
- # treat it as the one with paginated resources (i.e. ignore the repeated fields if any).
252
+ # The following steps are followed for this sub-heuristic:
253
+ # 0. Check the exception dictionary. If the method is there as key, use the value as the results field.
254
+ if REPEATED_FIELD_SPECIAL_DICTIONARY.key? @response.full_name
255
+ field_name = REPEATED_FIELD_SPECIAL_DICTIONARY[@response.full_name]
256
+ field = map_fields.find do |f|
257
+ f.name == field_name
258
+ end || repeated_fields.find do |f|
259
+ f.name == field_name
260
+ end
261
+
262
+ unless field
263
+ error_text = "A field of name \"#{field_name}\" is included in the special dictionary for " \
264
+ "the response type \"#{@response.full_name}\". However this field is not a map " \
265
+ "or repeated field. Failing, as the generator cannot continue."
266
+
267
+ raise ::Gapic::Model::ModelError, error_text
268
+ end
269
+
270
+ field
271
+ elsif map_fields.count == 1
272
+ # 1. If there is exactly one map field, that field is the results field.
177
273
  map_fields.first
178
- elsif repeated_fields.count == 1 && map_fields.empty?
179
- # If the response message contains only one repeated field,
180
- # treat that field as the one containing the paginated resources.
274
+ # NB: at this point the response contains either 0 or 2 map fields.
275
+ elsif repeated_fields.count.zero?
276
+ # 2. If there are no repeated fields there is no results field and we shall throw an error
277
+ error_text = "A response message \"#{@response.full_name}\" has a next page token field and " \
278
+ "is being evaluated as a candidate for pagination. However it has " \
279
+ "#{map_fields.count} (!= 1) map fields and no repeated fields. " \
280
+ "Failing, as the generator should not continue."
281
+
282
+ raise ::Gapic::Model::ModelError, error_text
283
+ elsif repeated_fields.count == 1
284
+ # 3. If there is exactly one repeated field, that field is the results field.
181
285
  repeated_fields.first
286
+ elsif repeated_fields.count == 2
287
+ # 4. If there are exactly 2 repeated fields, one is of message type, and the other is of type
288
+ # "String", the field of message type is the results field.
289
+ pagination_field = repeated_fields.find(&:message?)
290
+ string_field = repeated_fields.find { |f| f.type == :TYPE_STRING }
291
+
292
+ unless pagination_field && string_field
293
+ # At this point if there are 2 repeated fields of different configuration, or 3 or more repeated
294
+ # fields the method should have been in the exception dictionary (see step 0).
295
+ error_text = "A response message \"#{@response.full_name}\" has a next page token " \
296
+ "field and is being evaluated as a candidate for pagination. However it should have " \
297
+ "a configuration of exactly 2 repeated fields, one is of message type, and the other " \
298
+ "of type \"String\". Failing, as the generator cannot continue. \n" \
299
+ "As a developer, please evaluate the message that fails this heuristic and either " \
300
+ "change the heuristic or add the message to the special dictionary."
301
+
302
+ raise ::Gapic::Model::ModelError, error_text
303
+ end
304
+
305
+ pagination_field
306
+ else
307
+ # At this point there are 3 or more repeated fields, and the method should have been in the
308
+ # exception dictionary (see step 0).
309
+ error_text = "A response message \"#{@response.full_name}\" has a next page token " \
310
+ "field and is being evaluated as a candidate for pagination. However it has " \
311
+ "#{repeated_fields.count} (>= 3) repeated fields, and not in the special dictionary " \
312
+ "for exceptions. Failing, as the generator cannot continue. \n" \
313
+ "As a developer, please evaluate the message that fails this heuristic and either " \
314
+ "change the heuristic or add the message to the special dictionary."
315
+
316
+ raise ::Gapic::Model::ModelError, error_text
182
317
  end
183
- # If the response message contains more than one repeated field or does not have repeated fields at all
184
- # but has more than one map<string, ?> field, do not generate any paginated methods for such rpc.
185
318
  end
186
319
  end
187
320
 
321
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
322
+
188
323
  # @private
189
324
  FIELD_TYPE_MAPPING = {
190
325
  TYPE_DOUBLE: "::Float",
@@ -3,7 +3,7 @@ source "https://rubygems.org"
3
3
 
4
4
  gemspec
5
5
 
6
- gem "google-style", "~> 1.31.0"
6
+ gem "google-style", "~> 1.31.1"
7
7
  gem "minitest", "~> 5.22"
8
8
  gem "minitest-focus", "~> 1.4"
9
9
  gem "minitest-rg", "~> 5.3"
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.45.2
4
+ version: 0.45.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ernest Landrito
@@ -87,14 +87,14 @@ dependencies:
87
87
  requirements:
88
88
  - - "~>"
89
89
  - !ruby/object:Gem::Version
90
- version: 1.31.0
90
+ version: 1.31.1
91
91
  type: :runtime
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
95
  - - "~>"
96
96
  - !ruby/object:Gem::Version
97
- version: 1.31.0
97
+ version: 1.31.1
98
98
  email:
99
99
  - landrito@google.com
100
100
  - quartzmo@gmail.com