class Gapic::Rest::GrpcTranscoder
Internal doc go/actools-regapic-grpc-transcoding.
using a configuration of bindings.
Transcodes a proto request message into HTTP Rest call components
@private
def bind_uri_values! http_binding, request_hash
-
(Hash{String, String})
-
Parameters:
-
request_hash
(Hash
) -- -
http_binding
(Gapic::Rest::GrpcTranscoder::HttpBinding
) --
def bind_uri_values! http_binding, request_hash http_binding.field_bindings.to_h do |field_binding| field_path_camel = field_binding.field_path.split(".").map { |part| camel_name_for part }.join(".") field_value = extract_scalar_value! request_hash, field_path_camel, field_binding.regex if field_value field_value = field_value.split("/").map { |segment| percent_escape segment }.join("/") end [field_binding.field_path, field_value] end end
def build_query_params request_hash, prefix = ""
-
(Array{String, String})
- Query string params as key-value pairs.
Parameters:
-
prefix
(String
) -- A prefix to form the correct query parameter key. -
request_hash
(Hash
) --
def build_query_params request_hash, prefix = "" result = [] request_hash.each do |key, value| full_key_name = "#{prefix}#{key}" case value when ::Array value.each do |_val| result.push "#{full_key_name}=#{value}" end when ::Hash result += build_query_params value, "#{full_key_name}." else result.push "#{full_key_name}=#{value}" unless value.nil? end end result end
def camel_name_for attr_name
-
(String)
- Camel-cased parameter name.
Parameters:
-
attr_name
(String
) -- Parameter name.
def camel_name_for attr_name parts = attr_name.split "_" first_part = parts[0] other_parts = parts[1..] other_parts_pascal = other_parts.map(&:capitalize).join "#{first_part}#{other_parts_pascal}" end
def construct_body_query_params body_template, request_hash_without_uri, request
-
(Array{String, Array})
- A pair of body and query parameters.
Parameters:
-
request
(Object
) -- The GRPC request. -
request_hash_without_uri
(Hash
) -- -
body_template
(String, Nil
) -- The template for the body, e.g. `*`.
def construct_body_query_params body_template, request_hash_without_uri, request body = "" query_params = [] if body_template == "*" body = request_hash_without_uri.to_json elsif body_template && body_template != "" # Using a `request` here instead of `request_hash_without_uri` # because if `body` is bound to a message field, # the fields of the corresponding sub-message, # which were used when constructing the URI, should not be deleted # (as opposed to the case when `body` is `*`). # # The `request_hash_without_uri` at this point was mutated to delete these fields. # # Note 1: body template can only point to a top-level field. # Note 2: The field that body template points to can be null, in which case # an empty string should be sent. E.g. `Compute.Projects.SetUsageExportBucket`. request_body_field = request.send body_template.to_sym if request.respond_to? body_template.to_sym if request_body_field request_hash_without_uri.delete camel_name_for body_template body = request_body_field.to_json emit_defaults: true end query_params = build_query_params request_hash_without_uri else query_params = build_query_params request_hash_without_uri end [body, query_params] end
def expand_template template, bindings
-
(String)
- The expanded template.
Parameters:
-
bindings
(Hash{String, String}
) -- -
template
(String
) -- The Uri template.
def expand_template template, bindings result = template bindings.each do |name, value| result = result.gsub "{#{name}}", value end result end
def extract_scalar_value! request_hash, field_path, regex
-
(String, Nil)
- the field's string representation or nil.
Parameters:
-
regex
(Regexp
) -- A regex to match on the field's string representation. -
field_path
(String
) -- A path to the field, e.g. `foo.bar`. -
request_hash
(Hash
) --
def extract_scalar_value! request_hash, field_path, regex parent, name = find_value request_hash, field_path value = parent.delete name # Covers the case where in `foo.bar.baz`, `baz` is still a submessage or an array. return nil if value.is_a?(::Hash) || value.is_a?(::Array) value.to_s if value.to_s =~ regex end
def find_value request_hash, field_path
-
field_path
(String
) -- A path of the field, e.g. `foo.bar`. -
request_hash
(Hash
) -- A hash of the GRPC request or the sub-request.
def find_value request_hash, field_path path_split = field_path.split "." value_parent = nil value = request_hash last_field_name = nil path_split.each do |curr_field| # Covers the case when in `foo.bar.baz`, `bar` is not a submessage field # or is a submessage field initialized with nil. return {}, nil unless value.is_a? ::Hash value_parent = value last_field_name = curr_field value = value[curr_field] end [value_parent, last_field_name] end
def initialize bindings = nil
def initialize bindings = nil @bindings = bindings || [] end
def percent_escape str
-
(String)
- Escaped string.
Parameters:
-
str
(String
) -- String to escape.
def percent_escape str # `+` to represent spaces is not currently supported in Showcase server. CGI.escape(str).gsub("+", "%20") end
def transcode request
-
(Array)
- The components of the transcoded request.
Parameters:
-
request
(Object
) -- The GRPC request object
Other tags:
- Private: -
def transcode request # Using bindings in reverse here because of the "last one wins" rule @bindings.reverse.each do |http_binding| # The main reason we are using request.to_json here # is that the unset proto3_optional fields will not be # in that JSON, letting us skip the checks that would look like # `request.respond_to?("has_#{key}?".to_sym) && !request.send("has_#{key}?".to_sym)` # The reason we set emit_defaults: true is to avoid # having to figure out default values for the required # fields at a runtime. # # Make a new one for each binding because extract_scalar_value! is destructive request_hash = JSON.parse request.to_json emit_defaults: true uri_values = bind_uri_values! http_binding, request_hash next if uri_values.any? { |_, value| value.nil? } # Note that the body template can only point to a top-level field, # so there is no need to split the path. next if http_binding.body && http_binding.body != "*" && !(request.respond_to? http_binding.body.to_sym) method = http_binding.method uri = expand_template http_binding.template, uri_values body, query_params = construct_body_query_params http_binding.body, request_hash, request return method, uri, query_params, body end raise ::Gapic::Common::Error, "Request object does not match any transcoding template. Cannot form a correct REST call." end
def with_bindings uri_method:, uri_template:, matches: [], body: nil
-
(Gapic::Rest::GrpcTranscoder)
- The updated transcoder.
Parameters:
-
body
(String, Nil
) -- The body template, e.g. `*` or a field path. -
matches
(Array
) -- Variable bindings in an array. Every element -
uri_template
(String
) -- The string with uri template for the binding. -
uri_method
(Symbol
) -- The rest verb for the binding.
Other tags:
- Private: -
def with_bindings uri_method:, uri_template:, matches: [], body: nil binding = HttpBinding.create_with_validation(uri_method: uri_method, uri_template: uri_template, matches: matches, body: body) GrpcTranscoder.new @bindings + [binding] end