Skip to content

Commit

Permalink
Merge pull request #2109 from microsoft/feature/ruby-multi-valued-header
Browse files Browse the repository at this point in the history
 - adds support for multi-valued header in Ruby
  • Loading branch information
baywet authored Dec 29, 2022
2 parents dbc17c1 + 9d74250 commit cc91301
Show file tree
Hide file tree
Showing 14 changed files with 125 additions and 43 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added support for default properties values in Ruby. [#1725](https://github.com/microsoft/kiota/issues/1725)
- Added support for discriminated types deserialization (inheritance) in Ruby. [#1652](https://github.com/microsoft/kiota/issues/1652)
- Added support for error mapping in Ruby. [#1653](https://github.com/microsoft/kiota/issues/1653)
- Added support for multi-valued request headers in Ruby. [#2054](https://github.com/microsoft/kiota/issues/2054)

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
require_relative "microsoft_kiota_abstractions/api_client_builder"
require_relative "microsoft_kiota_abstractions/request_adapter"
require_relative "microsoft_kiota_abstractions/request_option"
require_relative "microsoft_kiota_abstractions/request_headers"
require_relative "microsoft_kiota_abstractions/request_information"
require_relative "microsoft_kiota_abstractions/version"
require_relative "microsoft_kiota_abstractions/serialization/parsable"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ def initialize(access_token_provider)
AUTHORIZATION_HEADER_KEY = 'Authorization'
def authenticate_request(request, additional_properties = {})
raise StandardError, 'Request cannot be null' if request.nil?
return if request.headers.key?(AUTHORIZATION_HEADER_KEY)

Fiber.new do
token = @access_token_provider.get_authorization_token(request.uri, additional_properties).resume
request.headers[AUTHORIZATION_HEADER_KEY] = "Bearer #{token}" unless token.nil? || token.empty?
end
request.headers.add(AUTHORIZATION_HEADER_KEY, "Bearer #{token}") unless token.nil? || token.empty?
end unless request.headers.get_all.key?(AUTHORIZATION_HEADER_KEY)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module MicrosoftKiotaAbstractions
class RequestHeaders
def initialize()
@headers = Hash.new
end
def add(key, value)
if key.nil? || key.empty? || value.nil? || value.empty? then
raise ArgumentError, 'key and value cannot be nil or empty'
end
existing_value = @headers[key]
if existing_value.nil? then
if value.kind_of?(Array) then
@headers[key] = value
else
@headers[key] = Array[value.to_s]
end
else
if value.kind_of?(Array) then
@headers[key] = existing_value | value
else
existing_value << value.to_s
end
end
end
def get(key)
if key.nil? || key.empty? then
raise ArgumentError, 'key cannot be nil or empty'
end
return @headers[key]
end
def remove(key)
if key.nil? || key.empty? then
raise ArgumentError, 'key cannot be nil or empty'
end
@headers.delete(key)
end
def clear()
@headers.clear()
end
def get_all()
return @headers
end
end
end
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
require 'uri'
require 'addressable/template'
require_relative 'http_method'
require_relative 'request_headers'

module MicrosoftKiotaAbstractions
class RequestInformation
attr_reader :content, :http_method
attr_accessor :url_template
attr_reader :content, :http_method, :headers
attr_accessor :url_template, :path_parameters, :query_parameters
@@binary_content_type = 'application/octet-stream'
@@content_type_header = 'Content-Type'
@@raw_url_key = 'request-raw-url'

def initialize()
@headers = RequestHeaders.new
@query_parameters = Hash.new
@path_parameters = Hash.new
end

def uri=(arg)
if arg.nil? || arg.empty?
Expand Down Expand Up @@ -77,35 +84,15 @@ def http_method=(method)
@http_method = HttpMethod::HTTP_METHOD[method]
end

def query_parameters
@query_parameters ||= Hash.new
end

def headers
@headers ||= Hash.new
end

def path_parameters
@path_parameters ||= Hash.new
end

def path_parameters=(value)
@path_parameters = value
end

def headers=(value)
@headers = value
end

def set_stream_content(value = $stdin)
@content = value
self.headers[@@content_type_header] = @@binary_content_type
@headers.add(@@content_type_header, @@binary_content_type)
end

def set_content_from_parsable(request_adapter, content_type, values)
begin
writer = request_adapter.get_serialization_writer_factory().get_serialization_writer(content_type)
headers[@@content_type_header] = content_type
@headers.add(@@content_type_header, content_type)
if values != nil && values.kind_of?(Array)
writer.write_collection_of_object_values(nil, values)
else
Expand All @@ -117,11 +104,8 @@ def set_content_from_parsable(request_adapter, content_type, values)
end
end

def set_headers_from_raw_object(h)
if !h
return
end
h.select{|x,y| self.headers[x.to_s] = y.to_s}
def add_headers_from_raw_object(h)
h.select{|x,y| @headers.add(x.to_s, y)} unless !h
end

def set_query_string_parameters_from_raw_object(q)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module MicrosoftKiotaAbstractions
VERSION = "0.10.0"
VERSION = "0.11.0"
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require 'microsoft_kiota_abstractions'
RSpec.describe MicrosoftKiotaAbstractions do
it "adds a request header to the request information" do
request_info = MicrosoftKiotaAbstractions::RequestInformation.new
request_info.headers.add("key", "value")
expect(request_info.headers.get_all.length).to eq(1)
expect(request_info.headers.get("key").length).to eq(1)
expect(request_info.headers.get("key").first).to eq("value")
end

it "adds a request header to the request information when one value is already present" do
request_info = MicrosoftKiotaAbstractions::RequestInformation.new
request_info.headers.add("key", "value")
request_info.headers.add("key", "value2")
expect(request_info.headers.get_all.length).to eq(1)
expect(request_info.headers.get("key").length).to eq(2)
expect(request_info.headers.get("key").first).to eq("value")
expect(request_info.headers.get("key").last).to eq("value2")
end

it "removes a request header from the request information" do
request_info = MicrosoftKiotaAbstractions::RequestInformation.new
request_info.headers.add("key", "value")
expect(request_info.headers.get_all.length).to eq(1)
request_info.headers.remove("key")
expect(request_info.headers.get_all.length).to eq(0)
end

it "doesnt fail when removing a value if none are present" do
request_info = MicrosoftKiotaAbstractions::RequestInformation.new
request_info.headers.remove("key")
expect(request_info.headers.get_all.length).to eq(0)
end

it "clears the request headers from the request information" do
request_info = MicrosoftKiotaAbstractions::RequestInformation.new
request_info.headers.add("key", "value")
expect(request_info.headers.get_all.length).to eq(1)
request_info.headers.clear
expect(request_info.headers.get_all.length).to eq(0)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']

spec.add_runtime_dependency 'microsoft_kiota_abstractions', '~> 0.10.0', '>= 0.10.0'
spec.add_runtime_dependency 'microsoft_kiota_abstractions', '~> 0.11.0', '>= 0.11.0'
spec.add_runtime_dependency 'oauth2', '~> 2.0'
spec.add_development_dependency 'rake', '~> 13.0'
spec.add_development_dependency 'rspec', '~> 3.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,17 @@ def get_request_from_request_info(request_info)
raise StandardError, 'unsupported http method'
end
request.path = request_info.uri
if request_info.headers.instance_of? Hash
unless request_info.headers.nil? then
request.headers = Faraday::Utils::Headers.new
request_info.headers.select{|k,v| request.headers[k] = v }
request_info.headers.get_all.select{|k,v|
if v.kind_of? Array then
request.headers[k] = v.join(',')
elsif v.kind_of? String then
request.headers[k] = v
else
request.headers[k] = v.to_s
end
}
end
request.body = request_info.content unless request_info.content.nil? || request_info.content.empty?
# TODO the json serialization writer returns a string at the moment, change to body_stream when this is fixed
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module MicrosoftKiotaFaraday
VERSION = '0.7.0'
VERSION = '0.8.0'
end
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
spec.bindir = 'bin'
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']
spec.add_runtime_dependency 'microsoft_kiota_abstractions', '~> 0.10.0', '>= 0.10.0'
spec.add_runtime_dependency 'microsoft_kiota_abstractions', '~> 0.11.0', '>= 0.11.0'
spec.add_runtime_dependency 'faraday', '~> 2.7', '>= 2.7.2'
spec.add_development_dependency 'rake', '~> 13.0'
spec.add_development_dependency 'rspec', '~> 3.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']

spec.add_runtime_dependency 'microsoft_kiota_abstractions', '~> 0.10.0', '>= 0.10.0'
spec.add_runtime_dependency 'microsoft_kiota_abstractions', '~> 0.11.0', '>= 0.11.0'
spec.add_runtime_dependency 'uuidtools'
spec.add_development_dependency 'rake', '~> 13.0'
spec.add_development_dependency 'rspec', '~> 3.0'
Expand Down
4 changes: 2 additions & 2 deletions src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req
$"request_info.path_parameters = {GetPropertyCall(urlTemplateParamsProperty, "''")}",
$"request_info.http_method = :{codeElement.HttpMethod?.ToString().ToUpperInvariant()}");
if(codeElement.AcceptedResponseTypes.Any())
writer.WriteLine($"request_info.headers['Accept'] = '{string.Join(", ", codeElement.AcceptedResponseTypes)}'");
writer.WriteLine($"request_info.headers.add('Accept', '{string.Join(", ", codeElement.AcceptedResponseTypes)}')");
if (requestParams.requestConfiguration != null)
{
var queryString = requestParams.QueryParameters;
Expand All @@ -268,7 +268,7 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req
writer.WriteLine($"unless {requestParams.requestConfiguration.Name.ToSnakeCase()}.nil?");
writer.IncreaseIndent();
if(headers != null)
writer.WriteLine($"request_info.set_headers_from_raw_object({requestParams.requestConfiguration.Name.ToSnakeCase()}.{headers.Name.ToSnakeCase()})");
writer.WriteLine($"request_info.add_headers_from_raw_object({requestParams.requestConfiguration.Name.ToSnakeCase()}.{headers.Name.ToSnakeCase()})");
if(queryString != null)
writer.WriteLine($"request_info.set_query_string_parameters_from_raw_object({requestParams.requestConfiguration.Name.ToSnakeCase()}.{queryString.Name.ToSnakeCase()})");
if (options != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ public void DoesntWriteFactorySwitchOnEmptyPropertyName() {
writer.Write(factoryMethod);
var result = tw.ToString();
Assert.DoesNotContain("mapping_value_node = parse_node.get_child_node(\"@odata.type\")", result);
Assert.DoesNotContain("unless (mapping_value_node.nil?) do", result);
Assert.DoesNotContain("unless mapping_value_node.nil? then", result);
Assert.DoesNotContain("mapping_value = mapping_value_node.get_string_value", result);
Assert.DoesNotContain("case mapping_value", result);
Assert.DoesNotContain("when \"ns.childmodel\"", result);
Expand Down Expand Up @@ -387,7 +387,7 @@ public void DoesntWriteFactorySwitchOnEmptyMappings() {
writer.Write(factoryMethod);
var result = tw.ToString();
Assert.DoesNotContain("mapping_value_node = parse_node.get_child_node(\"@odata.type\")", result);
Assert.DoesNotContain("unless (mapping_value_node.nil?) do", result);
Assert.DoesNotContain("unless mapping_value_node.nil? then", result);
Assert.DoesNotContain("mapping_value = mapping_value_node.get_string_value", result);
Assert.DoesNotContain("case mapping_value", result);
Assert.DoesNotContain("when \"ns.childmodel\"", result);
Expand All @@ -398,6 +398,7 @@ public void DoesntWriteFactorySwitchOnEmptyMappings() {
public void WritesRequestGeneratorBody() {
method.Kind = CodeMethodKind.RequestGenerator;
method.HttpMethod = HttpMethod.Get;
method.AcceptedResponseTypes = new () {"application/json"};
AddRequestProperties();
AddRequestBodyParameters();
writer.Write(method);
Expand All @@ -406,7 +407,9 @@ public void WritesRequestGeneratorBody() {
Assert.Contains("request_info.path_parameters", result);
Assert.Contains("request_info.url_template", result);
Assert.Contains("http_method = :GET", result);
Assert.Contains("request_info.headers.add('Accept', 'application/json')", result);
Assert.Contains("set_query_string_parameters_from_raw_object", result);
Assert.Contains("add_headers_from_raw_object", result);
Assert.Contains("add_request_options", result);
Assert.Contains("set_content_from_parsable", result);
Assert.Contains("return request_info", result);
Expand Down

0 comments on commit cc91301

Please sign in to comment.