gapic-generator 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/lib/gapic/generator/version.rb +1 -1
  4. data/lib/gapic/generators/default_generator.rb +2 -0
  5. data/lib/gapic/generators/default_generator_parameters.rb +3 -1
  6. data/lib/gapic/grpc_service_config/{service_config.rb → config.rb} +2 -2
  7. data/lib/gapic/grpc_service_config/parser.rb +8 -8
  8. data/lib/gapic/model/method/http_annotation.rb +123 -0
  9. data/lib/gapic/model/method/lro.rb +160 -0
  10. data/lib/gapic/model/method/routing.rb +263 -0
  11. data/lib/gapic/model/mixins.rb +181 -0
  12. data/lib/gapic/model/model_error.rb +26 -0
  13. data/lib/gapic/model/service/nonstandard_lro_provider.rb +293 -0
  14. data/lib/gapic/model.rb +22 -0
  15. data/lib/gapic/path_pattern/pattern.rb +46 -0
  16. data/lib/gapic/path_pattern/segment.rb +97 -9
  17. data/lib/gapic/presenters/gem_presenter.rb +34 -2
  18. data/lib/gapic/presenters/{service_config_presenter.rb → grpc_service_config_presenter.rb} +1 -1
  19. data/lib/gapic/presenters/method_presenter.rb +43 -3
  20. data/lib/gapic/presenters/method_rest_presenter.rb +33 -55
  21. data/lib/gapic/presenters/service/lro_client_presenter.rb +90 -0
  22. data/lib/gapic/presenters/service_presenter.rb +185 -3
  23. data/lib/gapic/presenters/service_rest_presenter.rb +81 -0
  24. data/lib/gapic/presenters/snippet_presenter.rb +1 -0
  25. data/lib/gapic/presenters.rb +3 -1
  26. data/lib/gapic/schema/api.rb +56 -2
  27. data/lib/gapic/schema/loader.rb +4 -1
  28. data/lib/gapic/schema/service_config_parser.rb +118 -0
  29. data/lib/gapic/schema/wrappers.rb +124 -1
  30. data/lib/google/api/auth.pb.rb +75 -0
  31. data/lib/google/api/backend.pb.rb +59 -0
  32. data/lib/google/api/billing.pb.rb +53 -0
  33. data/lib/google/api/context.pb.rb +47 -0
  34. data/lib/google/api/control.pb.rb +38 -0
  35. data/lib/google/api/documentation.pb.rb +56 -0
  36. data/lib/google/api/endpoint.pb.rb +42 -0
  37. data/lib/google/api/label.pb.rb +49 -0
  38. data/lib/google/api/launch_stage.pb.rb +37 -0
  39. data/lib/google/api/log.pb.rb +47 -0
  40. data/lib/google/api/logging.pb.rb +48 -0
  41. data/lib/google/api/metric.pb.rb +90 -0
  42. data/lib/google/api/monitored_resource.pb.rb +68 -0
  43. data/lib/google/api/monitoring.pb.rb +48 -0
  44. data/lib/google/api/quota.pb.rb +63 -0
  45. data/lib/google/api/routing.pb.rb +58 -0
  46. data/lib/google/api/service.pb.rb +90 -0
  47. data/lib/google/api/source_info.pb.rb +44 -0
  48. data/lib/google/api/system_parameter.pb.rb +51 -0
  49. data/lib/google/api/usage.pb.rb +47 -0
  50. data/lib/google/cloud/extended_operations.pb.rb +57 -0
  51. data/lib/google/protobuf/any.pb.rb +1 -1
  52. data/lib/google/protobuf/api.pb.rb +69 -0
  53. data/lib/google/protobuf/descriptor.pb.rb +1 -1
  54. data/lib/google/protobuf/duration.pb.rb +41 -0
  55. data/lib/google/protobuf/source_context.pb.rb +39 -0
  56. data/lib/google/protobuf/struct.pb.rb +65 -0
  57. data/lib/google/protobuf/type.pb.rb +128 -0
  58. data/lib/google/protobuf/wrappers.pb.rb +80 -0
  59. data/templates/default/gem/yardopts.erb +1 -1
  60. data/templates/default/lib/_package.erb +4 -0
  61. data/templates/default/lib/_service.erb +6 -0
  62. data/templates/default/service/client/_client.erb +20 -8
  63. data/templates/default/service/client/_nonstandard_lro.erb +57 -0
  64. data/templates/default/service/client/_self_configure_defaults.erb +2 -2
  65. data/templates/default/service/client/method/def/_options_defaults.erb +1 -8
  66. data/templates/default/service/client/method/def/_response.erb +2 -0
  67. data/templates/default/service/client/method/def/_response_nonstandard_lro.erb +23 -0
  68. data/templates/default/service/client/method/def/_routing_params.erb +36 -0
  69. data/templates/default/service/nonstandard_lro.erb +6 -0
  70. data/templates/default/service/rest/client/_client.erb +32 -5
  71. data/templates/default/service/rest/client/method/def/_response.erb +2 -2
  72. data/templates/default/service/rest/client/method/def/_response_nonstandard_lro.erb +23 -0
  73. data/templates/default/service/test/method/_assert_response.erb +3 -0
  74. metadata +61 -7
  75. data/templates/default/service/rest/client/method/def/_response_lro.erb +0 -7
@@ -0,0 +1,263 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 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/path_pattern"
18
+
19
+ module Gapic
20
+ module Model
21
+ module Method
22
+ ##
23
+ # Routing headers info determined from the proto method
24
+ #
25
+ # @!attribute [r] routing
26
+ # @return [::Google::Api::RoutingRule] Explicit routing annotation for the Api
27
+ class Routing
28
+ attr_reader :routing
29
+
30
+ ##
31
+ # @param routing [::Google::Api::RoutingRule] Explict routing annotation for the Api
32
+ # @param http [Gapic::Model::Method::HttpAnnotation] Model for the Http annotation
33
+ #
34
+ def initialize routing, http
35
+ @routing = routing
36
+ @http = http
37
+ end
38
+
39
+ ##
40
+ # Whether routing parameters are specified
41
+ #
42
+ # @return [Boolean]
43
+ def routing_params?
44
+ explicit_params? || implicit_params?
45
+ end
46
+
47
+ ##
48
+ # Whether an explicit routing annotation (`google.api.routing`) is present
49
+ #
50
+ # @return [Boolean]
51
+ def explicit_annotation?
52
+ !@routing.nil?
53
+ end
54
+
55
+ ##
56
+ # Whether an annotation that might contain implicit routing (`google.api.http`) is present
57
+ #
58
+ # @return [Boolean]
59
+ def implicit_annotation?
60
+ !@http.nil?
61
+ end
62
+
63
+ ##
64
+ # Whether explicit routing parameters are present
65
+ #
66
+ # @return [Boolean]
67
+ def explicit_params?
68
+ return false unless explicit_annotation?
69
+
70
+ explicit_params.any?
71
+ end
72
+
73
+ ##
74
+ # Explicit routing parameters, if present.
75
+ # Grouped by the routing key, in order, with the extraction and matching information.
76
+ #
77
+ # @return [Hash<String, Array>]
78
+ def explicit_params
79
+ all_explicit_params.group_by(&:key).transform_values do |params|
80
+ params.sort_by(&:order).to_a
81
+ end
82
+ end
83
+
84
+ ##
85
+ # Whether implict routing parameters from `google.api.http` annotation are present
86
+ #
87
+ # @return [Boolean]
88
+ def implicit_params?
89
+ @http.routing_params?
90
+ end
91
+
92
+ ##
93
+ # Implicit routing parameters.
94
+ # These strings are both field and routing header key names.
95
+ #
96
+ # @return [Array<String>]
97
+ def implicit_params
98
+ return {} unless implicit_params?
99
+ @http.routing_params
100
+ end
101
+
102
+ ##
103
+ # Full routing parameter information, including parsing order
104
+ # and matching/extraction patterns and regexes.
105
+ #
106
+ # @!attribute [r] order
107
+ # @return [Integer] Order of the parameter in the annotation
108
+ #
109
+ # @!attribute [r] field
110
+ # @return [String] Field to extract the routing header from
111
+ #
112
+ # @!attribute [r] raw_template
113
+ # @return [String, nil] Raw template as given in the annotation
114
+ #
115
+ # @!attribute [r] path_template
116
+ # @return [String] 'Processed' template, handling such cases as
117
+ # empty or nameless template
118
+ #
119
+ # @!attribute [r] key
120
+ # @return [String] Name of the key to add to the routing header
121
+ #
122
+ # @!attribute [r] value_pattern
123
+ # @return [String] The pattern of the value to be extracted
124
+ #
125
+ # @!attribute [r] field_pattern
126
+ # @return [String] The pattern of the full field (simplified)
127
+ # The field as a whole should match this pattern
128
+ # This pattern is simplified, stipped of the names of the
129
+ # resource id segments.
130
+ # (e.g. `collections/{resource_id=foo/*}` => `collections/foo/*`)
131
+ #
132
+ # @!attribute [r] field_regex_str
133
+ # @return [String] The regex matching the `field_pattern`
134
+ # (the regex form of the simplified pattern)
135
+ #
136
+ # @!attribute [r] field_full_regex_str
137
+ # @return [String] The regex matching the full unsimplified field pattern
138
+ # (it will contain the named capture corresponding to the
139
+ # resource id segment name)
140
+ #
141
+ class RoutingParameter
142
+ attr_reader :order
143
+ attr_reader :field
144
+ attr_reader :raw_template
145
+ attr_reader :path_template
146
+ attr_reader :key
147
+ attr_reader :value_pattern
148
+ attr_reader :field_pattern
149
+ attr_reader :field_regex_str
150
+ attr_reader :field_full_regex_str
151
+
152
+ ##
153
+ # @param routing_parameter [::Google::Api::RoutingParameter]
154
+ # Routing parameter annotation
155
+ #
156
+ # @param order [Integer]
157
+ # Order of this annotation among its peers
158
+ def initialize routing_parameter, order
159
+ @order = order
160
+ @field = routing_parameter.field
161
+ @raw_template = routing_parameter.path_template
162
+ @path_template = infer_template @raw_template, @field
163
+ @path_pattern = Gapic::PathPattern.parse @path_template
164
+
165
+ resource_segment = @path_pattern.segments.find(&:resource_id_segment?)
166
+
167
+ # Only one segment providing an argument and only one argument in the segment
168
+ # (no `{foo}~{bar}` segments)
169
+ valid = @path_pattern.segments.count(&:resource_id_segment?) == 1 &&
170
+ resource_segment.arguments.count == 1
171
+
172
+ unless valid
173
+ error_text = create_invalid_error_text @path_pattern, @raw_template
174
+ raise ModelError, error_text
175
+ end
176
+
177
+ @field_pattern = @path_pattern.simplified_pattern
178
+ @field_full_regex_str = to_field_pattern @path_pattern
179
+ @field_regex_str = to_field_pattern Gapic::PathPattern.parse(@field_pattern)
180
+
181
+ @key = resource_segment.arguments[0]
182
+ @value_pattern = resource_segment.resource_patterns[0]
183
+ end
184
+
185
+ ##
186
+ # Whether pattern matching is not needed
187
+ # since the patterns allow all strings
188
+ # @return [Boolean]
189
+ def pattern_matching_not_needed?
190
+ field_pattern == "**" && value_pattern == "**"
191
+ end
192
+
193
+ ##
194
+ # Whether the value to be added to the routing header
195
+ # is the value of the whole field
196
+ # @return [Boolean]
197
+ def value_is_full_field?
198
+ @path_pattern.segments.count == 1
199
+ end
200
+
201
+ private
202
+
203
+ # Makes a regex pattern match a field
204
+ # - adds markers for the beginning and end of the string
205
+ # - adds handling of an optional tail `/` if needed
206
+ # @param pattern [String]
207
+ # @return [String]
208
+ def to_field_pattern pattern
209
+ tail_slash_accept = if pattern.segments.last.simplified_pattern == "**"
210
+ ""
211
+ else
212
+ "/?"
213
+ end
214
+ "^#{pattern.to_regex_str}#{tail_slash_accept}$"
215
+ end
216
+
217
+ # Converts path template simplified forms into canonical
218
+ # ResourceId representations by adding a field as a Resource Id
219
+ # @param template [String]
220
+ # @param field [String]
221
+ # @return [String]
222
+ def infer_template template, field
223
+ if template.nil? || template.empty?
224
+ return "{#{field}=**}"
225
+ end
226
+
227
+ if template.strip == "**"
228
+ return "{#{field}=**}"
229
+ end
230
+
231
+ if template.strip == "*"
232
+ return "{#{field}=*}"
233
+ end
234
+
235
+ template
236
+ end
237
+
238
+ def create_invalid_error_text path_pattern, raw_template
239
+ reason = if path_pattern.segments.count(&:resource_id_segment?).zero?
240
+ "it contains no ResourceId (e.g. `{foo=*}`) segments"
241
+ elsif path_pattern.segments.count(&:resource_id_segment?) > 1
242
+ "it contains more than one ResourceId (e.g. `{foo=*}`) segments "
243
+ else
244
+ "it contains a multivariate ResourceId segment (e.g. `{foo}~{bar}`)"
245
+ end
246
+
247
+ "A routing header parameter with the path_template #{raw_template}\n " \
248
+ "is invalid: #{reason}"
249
+ end
250
+ end
251
+
252
+ private
253
+
254
+ # All explicit routing parameters in an array.
255
+ # @return [Array<RoutingParameter>]
256
+ def all_explicit_params
257
+ return [] unless explicit_annotation?
258
+ @routing.routing_parameters.each_with_index.map { |param, index| RoutingParameter.new param, index }.to_a
259
+ end
260
+ end
261
+ end
262
+ end
263
+ end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Google LLC
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Gapic
18
+ module Model
19
+ # Aggregated information about the mixin services
20
+ # that should be referenced from their gems in the
21
+ # generated client libraries
22
+ class Mixins
23
+ # LRO might be specified in the mixins but it is not generated
24
+ # as a mixin
25
+ LRO_SERVICE = "google.longrunning.Operations"
26
+
27
+ # Locations and Iam are generated as mixins
28
+ LOCATIONS_SERVICE = "google.cloud.location.Locations"
29
+ IAM_SERVICE = "google.iam.v1.IAMPolicy"
30
+
31
+ # @return [Enumerable<String>] List of services from the Api model
32
+ attr_accessor :api_services
33
+
34
+ ##
35
+ # @param api_services [Enumerable<String>]
36
+ # List of services from the Api model
37
+ # @param service_config [Google::Api::Service]
38
+ # The service config
39
+ def initialize api_services, service_config
40
+ @api_services = api_services
41
+ @service_config = service_config
42
+ end
43
+
44
+ # @return [Boolean] Whether there are any mix-in services
45
+ def mixins?
46
+ mixin_services.any?
47
+ end
48
+
49
+ # @return [Enumerable<Mixin>]
50
+ # List of Mixin objects, providing the information that is needed
51
+ # to add the mixin service references to the generated library
52
+ def mixins
53
+ @mixins ||= mixin_services.map { |service| create_mixin(service) }
54
+ end
55
+
56
+ # @return [Enumerable<String>] Full proto names of the mix-in services
57
+ def mixin_services
58
+ @mixin_services ||= services_in_config.select do |service|
59
+ service != LRO_SERVICE && !@api_services.include?(service)
60
+ end
61
+ end
62
+
63
+ # @return [Hash<String, String>]
64
+ # Aggregated dependencies for the mix-in services
65
+ def dependencies
66
+ @dependencies ||= mixins.reduce({}) { |deps, mixin| deps.merge mixin.dependency }
67
+ end
68
+
69
+ # Model of a single mixin service
70
+ # @!attribute [r] service
71
+ # Full name of the service
72
+ # @return [String]
73
+ # @!attribute [r] dependency
74
+ # Service dependencies, in the
75
+ # `{ gem_name => version pattern }` format
76
+ # @return [Hash<String, String>]
77
+ # @!attribute [r] require_str
78
+ # Path to `require` the client of the service
79
+ # @return [String]
80
+ # @!attribute [r] client_class_name
81
+ # Full name of the class of the client of the service
82
+ # @return [String]
83
+ # @!attribute [r] client_class_docname
84
+ # Name of the class as it should appear in the documentation
85
+ # @return [String]
86
+ # @!attribute [r] client_var_name
87
+ # Name for the variable for the client of the
88
+ # mixin service to use when generating library's service
89
+ # @return [String]
90
+ class Mixin
91
+ attr_reader :service
92
+ attr_reader :dependency
93
+ attr_reader :require_str
94
+ attr_reader :client_class_name
95
+ attr_reader :client_class_docname
96
+ attr_reader :client_var_name
97
+
98
+ # @param service [String]
99
+ # Full name of the service
100
+ # @param dependency [Hash<String, String>]
101
+ # Service dependencies, in the
102
+ # `{ gem_name => version pattern }` format
103
+ # @param require_str [String]
104
+ # Path to require the client of the service
105
+ # @param client_class_name [String]
106
+ # Full name of the class of the client of the service
107
+ # @param client_var_name [String]
108
+ # Name for the variable for the client of the mixin service
109
+ # to use when generating library's service
110
+ def initialize service, dependency, require_str, client_class_name, client_var_name
111
+ @service = service
112
+ @dependency = dependency
113
+ @require_str = require_str
114
+ @client_class_name = client_class_name
115
+ @client_class_docname = client_class_name # For mixins, the doc name should be the full class name
116
+ @client_var_name = client_var_name
117
+ end
118
+
119
+ # @return [String] The description to place in the comments to the
120
+ # mixin's attribute in the library services's client class
121
+ def service_description
122
+ "mix-in of the #{client_class_name.split('::')[-2]}"
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ # @return [Enumerable<String>] Names of all services that are specified
129
+ # in the Service Config. These include mixin services, as well as
130
+ # some, all, or none of the library's own services
131
+ def services_in_config
132
+ return [] unless @service_config&.apis
133
+ @service_config.apis.map(&:name)
134
+ end
135
+
136
+ # Since mixins are scope-limited to a couple of services, it is easier to
137
+ # have these in lookup tables than to construct a ServicePresenter
138
+
139
+ SERVICE_TO_DEPENDENCY = {
140
+ LOCATIONS_SERVICE => { "google-cloud-location" => "~> 0.1" },
141
+ IAM_SERVICE => { "google-iam-v1" => "~> 0.1" }
142
+ }.freeze
143
+
144
+ SERVICE_TO_REQUIRE_STR = {
145
+ LOCATIONS_SERVICE => "google/cloud/location",
146
+ IAM_SERVICE => "google/iam/v1/iam_policy"
147
+ }.freeze
148
+
149
+ SERVICE_TO_CLIENT_CLASS_NAME = {
150
+ LOCATIONS_SERVICE => "Google::Cloud::Location::Locations::Client",
151
+ IAM_SERVICE => "Google::Iam::V1::IAMPolicy::Client"
152
+ }.freeze
153
+
154
+ SERVICE_TO_CLIENT_ATTR_NAME = {
155
+ LOCATIONS_SERVICE => "location_client",
156
+ IAM_SERVICE => "iam_policy_client"
157
+ }.freeze
158
+
159
+ # @param service [String] full grpc name of the service
160
+ # @raise [ModelError]
161
+ # @return [Mixin]
162
+ def create_mixin service
163
+ unless SERVICE_TO_DEPENDENCY.key?(service) &&
164
+ SERVICE_TO_REQUIRE_STR.key?(service) &&
165
+ SERVICE_TO_CLIENT_CLASS_NAME.key?(service)
166
+ SERVICE_TO_CLIENT_ATTR_NAME.key? service
167
+
168
+ error_text = "A mixin service #{service} is specified in service config, but " \
169
+ "the Generator does not know of it."
170
+ raise ModelError, error_text
171
+ end
172
+
173
+ Mixin.new service,
174
+ SERVICE_TO_DEPENDENCY[service],
175
+ SERVICE_TO_REQUIRE_STR[service],
176
+ SERVICE_TO_CLIENT_CLASS_NAME[service],
177
+ SERVICE_TO_CLIENT_ATTR_NAME[service]
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Google LLC
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Gapic
18
+ module Model
19
+ ##
20
+ # Represents an error due to inconsistency in the Model
21
+ # that the Generator is attempting to Generate
22
+ #
23
+ class ModelError < StandardError
24
+ end
25
+ end
26
+ end