From 4ba5d7bb8fa70696ae748459180aac61cc055476 Mon Sep 17 00:00:00 2001 From: Zi Date: Sat, 26 Apr 2014 09:38:02 +0800 Subject: [PATCH 1/2] Fixed all unit specs and broke all features specs because of different implementation of BasicAuth --- lib/ruby_odata.rb | 5 +- lib/ruby_odata/resource.rb | 149 +++++++++++++++++++++++++++++++++++ lib/ruby_odata/service.rb | 58 +++++++------- ruby_odata.gemspec | 3 +- spec/revised_service_spec.rb | 13 ++- spec/service_spec.rb | 46 ++++++----- 6 files changed, 222 insertions(+), 52 deletions(-) create mode 100644 lib/ruby_odata/resource.rb diff --git a/lib/ruby_odata.rb b/lib/ruby_odata.rb index ef44736..996e009 100644 --- a/lib/ruby_odata.rb +++ b/lib/ruby_odata.rb @@ -7,7 +7,9 @@ require "active_support/inflector" require "active_support/core_ext" require "cgi" -require "rest_client" +require "typhoeus" +require "typhoeus/adapters/faraday" +require "faraday" require "nokogiri" require "bigdecimal" require "bigdecimal/util" @@ -19,5 +21,6 @@ require lib + "/ruby_odata/query_builder" require lib + "/ruby_odata/class_builder" require lib + "/ruby_odata/operation" +require lib + "/ruby_odata/resource" require lib + "/ruby_odata/service" require lib + "/ruby_odata/helpers" diff --git a/lib/ruby_odata/resource.rb b/lib/ruby_odata/resource.rb new file mode 100644 index 0000000..217d64a --- /dev/null +++ b/lib/ruby_odata/resource.rb @@ -0,0 +1,149 @@ +module OData + class Resource + attr_reader :url, :options, :block + + def initialize(url, options={}, backwards_compatibility=nil, &block) + @url = url + @block = block + if options.class == Hash + @options = options + else # compatibility with previous versions + @options = { :user => options, :password => backwards_compatibility } + end + + @conn = Faraday.new(:url => url) do |faraday| + faraday.adapter :typhoeus + faraday.response :raise_error + + faraday.options.timeout = timeout if timeout + faraday.options.open_timeout = open_timeout if open_timeout + + faraday.headers = (faraday.headers || {}).merge(@options[:headers] || {}) + faraday.headers = (faraday.headers).merge({ + :accept => '*/*; q=0.5, application/xml', + :accept_encoding => 'gzip, deflate', + }) + + if user + faraday.basic_auth user, password # this adds to headers so must be behind + end + end + + @conn.headers.delete :user_agent + end + + def get(additional_headers={}) + @conn.get do |req| + req.url url + req.headers = (headers || {}).merge(additional_headers) + end + end + + def head(additional_headers={}) + @conn.head do |req| + req.url url + req.headers = (headers || {}).merge(additional_headers) + end + end + + def post(payload, additional_headers={}) + @conn.post do |req| + req.url url + req.headers = (headers || {}).merge(additional_headers) + req.body = payload + end + end + + def put(payload, additional_headers={}) + @conn.put do |req| + req.url url + req.headers = (headers || {}).merge(additional_headers) + req.body = payload + end + end + + def patch(payload, additional_headers={}) + @conn.patch do |req| + req.url url + req.headers = (headers || {}).merge(additional_headers) + req.body = payload + end + end + + def delete(additional_headers={}) + @conn.delete do |req| + req.url url + req.headers = (headers || {}).merge(additional_headers) + end + end + + def to_s + url + end + + def user + options[:user] + end + + def password + options[:password] + end + + def headers + @conn.headers || {} + end + + def timeout + options[:timeout] + end + + def open_timeout + options[:open_timeout] + end + + # Construct a subresource, preserving authentication. + # + # Example: + # + # site = RestClient::Resource.new('http://example.com', 'adam', 'mypasswd') + # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain' + # + # This is especially useful if you wish to define your site in one place and + # call it in multiple locations: + # + # def orders + # RestClient::Resource.new('http://example.com/orders', 'admin', 'mypasswd') + # end + # + # orders.get # GET http://example.com/orders + # orders['1'].get # GET http://example.com/orders/1 + # orders['1/items'].delete # DELETE http://example.com/orders/1/items + # + # Nest resources as far as you want: + # + # site = RestClient::Resource.new('http://example.com') + # posts = site['posts'] + # first_post = posts['1'] + # comments = first_post['comments'] + # comments.post 'Hello', :content_type => 'text/plain' + # + def [](suburl, &new_block) + case + when block_given? then self.class.new(concat_urls(url, suburl), options, &new_block) + when block then self.class.new(concat_urls(url, suburl), options, &block) + else + self.class.new(concat_urls(url, suburl), options) + end + end + + def concat_urls(url, suburl) # :nodoc: + url = url.to_s + suburl = suburl.to_s + if url.slice(-1, 1) == '/' or suburl.slice(0, 1) == '/' + url + suburl + else + "#{url}/#{suburl}" + end + end + end +end diff --git a/lib/ruby_odata/service.rb b/lib/ruby_odata/service.rb index 3f5db17..00ce42a 100644 --- a/lib/ruby_odata/service.rb +++ b/lib/ruby_odata/service.rb @@ -9,7 +9,7 @@ class Service # @option options [String] :username for http basic auth # @option options [String] :password for http basic auth # @option options [Object] :verify_ssl false if no verification, otherwise mode (OpenSSL::SSL::VERIFY_PEER is default) - # @option options [Hash] :rest_options a hash of rest-client options that will be passed to all RestClient::Resource.new calls + # @option options [Hash] :rest_options a hash of rest-client options that will be passed to all OData::Resource.new calls # @option options [Hash] :additional_params a hash of query string params that will be passed on all calls # @option options [Boolean, true] :eager_partial true if queries should consume partial feeds until the feed is complete, false if explicit calls to next must be performed def initialize(service_uri, options = {}) @@ -97,12 +97,12 @@ def save_changes # @raise [ServiceError] if there is an error when talking to the service def execute begin - @response = RestClient::Resource.new(build_query_uri, @rest_options).get + @response = OData::Resource.new(build_query_uri, @rest_options).get rescue Exception => e handle_exception(e) end return Integer(@response) if @response =~ /^\d+$/ - handle_collection_result(@response) + handle_collection_result(@response.body) end # Overridden to identify methods handled by method_missing @@ -147,8 +147,8 @@ def load_property(obj, nav_prop) raise NotSupportedError, "You cannot load a property on an entity that isn't tracked" if obj.send(:__metadata).nil? raise ArgumentError, "'#{nav_prop}' is not a valid navigation property" unless obj.respond_to?(nav_prop.to_sym) raise ArgumentError, "'#{nav_prop}' is not a valid navigation property" unless @class_metadata[obj.class.to_s][nav_prop].nav_prop - results = RestClient::Resource.new(build_load_property_uri(obj, nav_prop), @rest_options).get - prop_results = build_classes_from_result(results) + results = OData::Resource.new(build_load_property_uri(obj, nav_prop), @rest_options).get + prop_results = build_classes_from_result(results.body) obj.send "#{nav_prop}=", (singular?(nav_prop) ? prop_results.first : prop_results) end @@ -224,7 +224,7 @@ def set_options!(options) @rest_options.merge!(options[:rest_options] || {}) @additional_params = options[:additional_params] || {} @namespace = options[:namespace] - @json_type = options[:json_type] || :json + @json_type = options[:json_type] || 'application/json' end def default_instance_vars! @@ -236,7 +236,7 @@ def default_instance_vars! end def set_namespaces - @edmx = Nokogiri::XML(RestClient::Resource.new(build_metadata_uri, @rest_options).get) + @edmx = Nokogiri::XML(OData::Resource.new(build_metadata_uri, @rest_options).get.body) @ds_namespaces = { "m" => "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", "edmx" => "http://schemas.microsoft.com/ado/2007/06/edmx", @@ -376,8 +376,8 @@ def handle_collection_result(result) def handle_exception(e) raise e unless defined? e.response - code = e.http_code - error = Nokogiri::XML(e.response) + code = e.response[:status] + error = Nokogiri::XML(e.response[:body]) message = if error.xpath("m:error/m:message", @ds_namespaces).first error.xpath("m:error/m:message", @ds_namespaces).first.content @@ -509,8 +509,8 @@ def extract_partial(doc) def handle_partial if @next_uri - result = RestClient::Resource.new(@next_uri, @rest_options).get - results = handle_collection_result(result) + result = OData::Resource.new(@next_uri, @rest_options).get + results = handle_collection_result(result.body) end results end @@ -600,26 +600,26 @@ def single_save(operation) if operation.kind == "Add" save_uri = build_save_uri(operation) json_klass = operation.klass.to_json(:type => :add) - post_result = RestClient::Resource.new(save_uri, @rest_options).post json_klass, {:content_type => @json_type} - return build_classes_from_result(post_result) + post_result = OData::Resource.new(save_uri, @rest_options).post json_klass, {:content_type => @json_type} + return build_classes_from_result(post_result.body) elsif operation.kind == "Update" update_uri = build_resource_uri(operation) json_klass = operation.klass.to_json - update_result = RestClient::Resource.new(update_uri, @rest_options).put json_klass, {:content_type => @json_type} - return (update_result.code == 204) + update_result = OData::Resource.new(update_uri, @rest_options).put json_klass, {:content_type => @json_type} + return (update_result.status == 204) elsif operation.kind == "Delete" delete_uri = build_resource_uri(operation) - delete_result = RestClient::Resource.new(delete_uri, @rest_options).delete - return (delete_result.code == 204) + delete_result = OData::Resource.new(delete_uri, @rest_options).delete + return (delete_result.status == 204) elsif operation.kind == "AddLink" save_uri = build_add_link_uri(operation) json_klass = operation.child_klass.to_json(:type => :link) - post_result = RestClient::Resource.new(save_uri, @rest_options).post json_klass, {:content_type => @json_type} + post_result = OData::Resource.new(save_uri, @rest_options).post json_klass, {:content_type => @json_type} # Attach the child to the parent - link_child_to_parent(operation) if (post_result.code == 204) + link_child_to_parent(operation) if (post_result.status == 204) - return(post_result.code == 204) + return(post_result.status == 204) end end @@ -633,11 +633,11 @@ def batch_save(operations) batch_uri = build_batch_uri body = build_batch_body(operations, batch_num, changeset_num) - result = RestClient::Resource.new( batch_uri, @rest_options).post body, {:content_type => "multipart/mixed; boundary=batch_#{batch_num}"} + result = OData::Resource.new( batch_uri, @rest_options).post body, {:content_type => "multipart/mixed; boundary=batch_#{batch_num}"} # TODO: More result validation needs to be done. # The result returns HTTP 202 even if there is an error in the batch - return (result.code == 202) + return (result.status == 202) end def build_batch_body(operations, batch_num, changeset_num) # Header @@ -827,17 +827,17 @@ def execute_import_function(name, *args) func[:parameters].keys.each_with_index { |key, i| params[key] = args[0][i] } unless func[:parameters].nil? function_uri = build_function_import_uri(name, params) - result = RestClient::Resource.new(function_uri, @rest_options).send(func[:http_method].downcase, {}) + result = OData::Resource.new(function_uri, @rest_options).send(func[:http_method].downcase, {}) # Is this a 204 (No content) result? - return true if result.code == 204 + return true if result.status == 204 # No? Then we need to parse the results. There are 4 kinds... if func[:return_type] == Array # a collection of entites - return build_classes_from_result(result) if @classes.include?(func[:inner_return_type].to_s) + return build_classes_from_result(result.body) if @classes.include?(func[:inner_return_type].to_s) # a collection of native types - elements = Nokogiri::XML(result).xpath("//ds:element", @ds_namespaces) + elements = Nokogiri::XML(result.body).xpath("//ds:element", @ds_namespaces) results = [] elements.each do |e| results << parse_primative_type(e.content, func[:inner_return_type]) @@ -847,18 +847,18 @@ def execute_import_function(name, *args) # a single entity if @classes.include?(func[:return_type].to_s) - entry = Nokogiri::XML(result).xpath("atom:entry[not(ancestor::atom:entry)]", @ds_namespaces) + entry = Nokogiri::XML(result.body).xpath("atom:entry[not(ancestor::atom:entry)]", @ds_namespaces) return entry_to_class(entry) end # or a single native type unless func[:return_type].nil? - e = Nokogiri::XML(result).xpath("/*").first + e = Nokogiri::XML(result.body).xpath("/*").first return parse_primative_type(e.content, func[:return_type]) end # Nothing could be parsed, so just return if we got a 200 or not - return (result.code == 200) + return (result.status == 200) end # Helpers diff --git a/ruby_odata.gemspec b/ruby_odata.gemspec index d2e7921..746046c 100644 --- a/ruby_odata.gemspec +++ b/ruby_odata.gemspec @@ -20,7 +20,8 @@ Gem::Specification.new do |s| s.add_dependency("addressable", ">= 2.3.4") s.add_dependency("i18n", "~> 0.6.0") s.add_dependency("activesupport", ">= 3.0.0") - s.add_dependency("rest-client", ">= 1.5.1") + s.add_dependency("typhoeus") + s.add_dependency("faraday") s.add_dependency("nokogiri", ">= 1.4.2") s.add_development_dependency("rake", "0.9.2") diff --git a/spec/revised_service_spec.rb b/spec/revised_service_spec.rb index b3448c4..a58d4ab 100644 --- a/spec/revised_service_spec.rb +++ b/spec/revised_service_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require 'base64' module OData @@ -285,11 +286,17 @@ module OData end describe "Dual Namespaces" do + let(:username) { "xxxx\\yyyy" } + let(:password) { "zzzz" } + before(:all) do - stub_request(:get, "http://xxxx%5Cyyyy:zzzz@test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). + auth_string = "#{username}:#{password}" + authorization_header = { authorization: "Basic #{Base64::encode64(auth_string).strip}" } + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS.merge(authorization_header)). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center_v2.xml", __FILE__)), :headers => {}) end + after(:all) do VMM.constants.each do |constant| VMM.send :remove_const, constant @@ -300,4 +307,4 @@ module OData lambda { OData::Service.new "http://test.com/test.svc/", { :username => "xxxx\\yyyy", :password=> "zzzz", :verify_ssl => false, :namespace => "VMM" } }.should_not raise_error end end -end \ No newline at end of file +end diff --git a/spec/service_spec.rb b/spec/service_spec.rb index 1cf8973..c1bff71 100644 --- a/spec/service_spec.rb +++ b/spec/service_spec.rb @@ -721,7 +721,7 @@ module OData svc.save_changes a_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products"). with(:body => { "uri" => "http://test.com/test.svc/Products(1)" }, - :headers => {'Content-Type' => 'application/json'}).should have_been_made + :headers => DEFAULT_HEADERS.merge({'Content-Type' => 'application/json'})).should have_been_made end it "should add the child to the parent's navigation property on a single_save" do @@ -778,16 +778,19 @@ module OData end describe "JSON serialization of objects" do + let(:username) { "blabla" } + let(:password) { "" } + before(:each) do # Required for the build_classes method - stub_request(:get, "http://blabla:@test.com/test.svc/$metadata"). + stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center.xml", __FILE__)), :headers => {}) - stub_request(:get, "http://blabla:@test.com/test.svc/VirtualMachines"). + stub_request(:get, "http://test.com/test.svc/VirtualMachines"). with(:headers => DEFAULT_HEADERS). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/virtual_machines.xml", __FILE__)), :headers => {}) - svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" } + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } svc.VirtualMachines results = svc.execute @json = results.first.as_json @@ -810,22 +813,29 @@ module OData end describe "handling of Microsoft System Center 2012" do + let(:username) { "blabla" } + let(:password) { "" } + before(:each) do + auth_string = "#{username}:#{password}" + authorization_header = { authorization: "Basic #{Base64::encode64(auth_string).strip}" } + headers = DEFAULT_HEADERS.merge(authorization_header) + # Required for the build_classes method - stub_request(:get, "http://blabla:@test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => headers). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center.xml", __FILE__)), :headers => {}) - stub_request(:get, "http://blabla:@test.com/test.svc/VirtualMachines"). - with(:headers => DEFAULT_HEADERS). + stub_request(:get, "http://test.com/test.svc/VirtualMachines"). + with(:headers => headers). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/virtual_machines.xml", __FILE__)), :headers => {}) - stub_request(:get, "http://blabla:@test.com/test.svc/HardwareProfiles?$filter=Memory%20eq%203500"). - with(:headers => DEFAULT_HEADERS). + stub_request(:get, "http://test.com/test.svc/HardwareProfiles?$filter=Memory%20eq%203500"). + with(:headers => headers). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/hardware_profiles.xml", __FILE__)), :headers => {}) - stub_request(:get, "http://blabla:@test.com/test.svc/VMTemplates"). - with(:headers => DEFAULT_HEADERS). + stub_request(:get, "http://test.com/test.svc/VMTemplates"). + with(:headers => headers). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/vm_templates.xml", __FILE__)), :headers => {}) end @@ -836,7 +846,7 @@ module OData end it "should successfully parse null valued string properties" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" } + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } svc.VirtualMachines results = svc.execute results.first.should be_a_kind_of(VMM::VirtualMachine) @@ -844,21 +854,21 @@ module OData end it "should successfully return a virtual machine" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" } + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } svc.VirtualMachines results = svc.execute results.first.should be_a_kind_of(VMM::VirtualMachine) end it "should successfully return a hardware profile for results that include a collection of complex types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" } + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } svc.HardwareProfiles.filter("Memory eq 3500") results = svc.execute results.first.should be_a_kind_of(VMM::HardwareProfile) end it "should successfully return a collection of complex types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" } + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } svc.HardwareProfiles.filter("Memory eq 3500") results = svc.execute granted_list = results.first.GrantedToList @@ -869,14 +879,14 @@ module OData it "should successfully return results that include a collection of Edm types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" } + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } svc.VMTemplates results = svc.execute results.first.should be_a_kind_of(VMM::VMTemplate) end it "should successfully return a collection of Edm types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" } + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } svc.VMTemplates results = svc.execute boot_order = results.first.BootOrder From 80434c993e1f14352b979c9c6e3c05297c152f04 Mon Sep 17 00:00:00 2001 From: Zi Date: Sun, 27 Apr 2014 12:37:46 +0800 Subject: [PATCH 2/2] WIP fixing feature specs --- features/basic_auth.feature | 26 +++---- .../basic_auth_protected_resource.yml | 4 +- .../cassettes/cucumber_tags/basic_auth.yml | 16 +++- features/cassettes/cucumber_tags/ssl.yml | 12 ++- features/service.feature | 74 +++++++++---------- features/step_definitions/service_steps.rb | 3 + lib/ruby_odata/resource.rb | 2 +- ruby_odata.gemspec | 2 + 8 files changed, 80 insertions(+), 59 deletions(-) diff --git a/features/basic_auth.feature b/features/basic_auth.feature index be10eea..5456efd 100644 --- a/features/basic_auth.feature +++ b/features/basic_auth.feature @@ -5,19 +5,19 @@ Background: Given a HTTP BasicAuth ODataService exists using username "admin" and password "passwd" And blueprints exist for the service -Scenario: Service should respond to valid collections - Then I should be able to call "Products" on the service + Scenario: Service should respond to valid collections + Then I should be able to call "Products" on the service -Scenario: Entity should fill values on protected resource - Given I call "AddToCategories" on the service with a new "Category" object with Name: "Auth Test Category" - And I save changes - And I call "Categories" on the service with args: "1" - When I run the query within a cassette named "basic_auth_protected_resource" - Then the method "Id" on the first result should equal: "1" - And the method "Name" on the first result should equal: "Auth Test Category" + Scenario: Entity should fill values on protected resource + Given I call "AddToCategories" on the service with a new "Category" object with Name: "Auth Test Category" + And I save changes + And I call "Categories" on the service with args: "1" + When I run the query within a cassette named "basic_auth_protected_resource" + Then the method "Id" on the first result should equal: "1" + And the method "Name" on the first result should equal: "Auth Test Category" -Scenario: Should get 401 if invalid credentials provided to protected URL - Given a HTTP BasicAuth ODataService exists using username "admin" and password "bad_pwd" it should throw an exception with message "401 Unauthorized" + Scenario: Should get 401 if invalid credentials provided to protected URL + Given a HTTP BasicAuth ODataService exists using username "admin" and password "bad_pwd" it should throw an exception with message "401 Unauthorized" -Scenario: Should get 401 if no credentials provided to protected URL - Given a HTTP BasicAuth ODataService exists it should throw an exception with message "401 Unauthorized" + Scenario: Should get 401 if no credentials provided to protected URL + Given a HTTP BasicAuth ODataService exists it should throw an exception with message "401 Unauthorized" diff --git a/features/cassettes/basic_auth_protected_resource.yml b/features/cassettes/basic_auth_protected_resource.yml index b871207..e7b2f80 100644 --- a/features/cassettes/basic_auth_protected_resource.yml +++ b/features/cassettes/basic_auth_protected_resource.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: http://admin:passwd@win7dev:8989/SampleService/BasicAuth/RubyOData.svc/Categories(1) + uri: http://win7dev:8989/SampleService/BasicAuth/RubyOData.svc/Categories(1) body: encoding: US-ASCII string: '' @@ -11,6 +11,8 @@ http_interactions: - ! '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate + Authorization: + - Basic YWRtaW46cGFzc3dk User-Agent: - Ruby response: diff --git a/features/cassettes/cucumber_tags/basic_auth.yml b/features/cassettes/cucumber_tags/basic_auth.yml index ff5124e..07f1738 100644 --- a/features/cassettes/cucumber_tags/basic_auth.yml +++ b/features/cassettes/cucumber_tags/basic_auth.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: post - uri: http://admin:passwd@win7dev:8989/SampleService/BasicAuth/RubyOData.svc/Categories + uri: http://win7dev:8989/SampleService/BasicAuth/RubyOData.svc/Categories body: encoding: ASCII-8BIT string: ! '{"Name":"Auth Test Category","AuditFields":{"CreatedBy":"Machinist"}}' @@ -15,6 +15,8 @@ http_interactions: - application/json Content-Length: - '69' + Authorization: + - Basic YWRtaW46cGFzc3dk User-Agent: - Ruby response: @@ -62,7 +64,7 @@ http_interactions: recorded_at: Tue, 07 Aug 2012 21:21:25 GMT - request: method: get - uri: http://admin:passwd@win7dev:8989/SampleService/BasicAuth/RubyOData.svc/Categories(1) + uri: http://win7dev:8989/SampleService/BasicAuth/RubyOData.svc/Categories(1) body: encoding: US-ASCII string: '' @@ -71,6 +73,8 @@ http_interactions: - ! '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate + Authorization: + - Basic YWRtaW46cGFzc3dk User-Agent: - Ruby response: @@ -116,7 +120,7 @@ http_interactions: recorded_at: Tue, 07 Aug 2012 21:21:25 GMT - request: method: get - uri: http://admin:bad_pwd@win7dev:8989/SampleService/BasicAuth/RubyOData.svc/$metadata + uri: http://win7dev:8989/SampleService/BasicAuth/RubyOData.svc/$metadata body: encoding: US-ASCII string: '' @@ -125,6 +129,8 @@ http_interactions: - ! '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate + Authorization: + - Basic YWRtaW46YmFkX3B3ZA== User-Agent: - Ruby response: @@ -163,7 +169,7 @@ http_interactions: recorded_at: Tue, 07 Aug 2012 21:21:25 GMT - request: method: get - uri: http://admin:passwd@win7dev:8989/SampleService/BasicAuth/RubyOData.svc/$metadata + uri: http://win7dev:8989/SampleService/BasicAuth/RubyOData.svc/$metadata body: encoding: US-ASCII string: '' @@ -172,6 +178,8 @@ http_interactions: - ! '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate + Authorization: + - Basic YWRtaW46cGFzc3dk User-Agent: - Ruby response: diff --git a/features/cassettes/cucumber_tags/ssl.yml b/features/cassettes/cucumber_tags/ssl.yml index df088b1..153b7b2 100644 --- a/features/cassettes/cucumber_tags/ssl.yml +++ b/features/cassettes/cucumber_tags/ssl.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: post - uri: https://admin:passwd@win7dev:44300/SampleService/BasicAuth/RubyOData.svc/Categories + uri: https://win7dev:44300/SampleService/BasicAuth/RubyOData.svc/Categories body: encoding: ASCII-8BIT string: ! '{"Name":"Auth Test Category","AuditFields":{"CreatedBy":"Machinist"}}' @@ -15,6 +15,8 @@ http_interactions: - application/json Content-Length: - '69' + Authorization: + - Basic YWRtaW46cGFzc3dk User-Agent: - Ruby response: @@ -62,7 +64,7 @@ http_interactions: recorded_at: Tue, 07 Aug 2012 21:21:57 GMT - request: method: get - uri: https://admin:passwd@win7dev:44300/SampleService/BasicAuth/RubyOData.svc/Categories(1) + uri: https://win7dev:44300/SampleService/BasicAuth/RubyOData.svc/Categories(1) body: encoding: US-ASCII string: '' @@ -71,6 +73,8 @@ http_interactions: - ! '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate + Authorization: + - Basic YWRtaW46cGFzc3dk User-Agent: - Ruby response: @@ -116,7 +120,7 @@ http_interactions: recorded_at: Tue, 07 Aug 2012 21:21:57 GMT - request: method: get - uri: https://admin:passwd@win7dev:44300/SampleService/BasicAuth/RubyOData.svc/$metadata + uri: https://win7dev:44300/SampleService/BasicAuth/RubyOData.svc/$metadata body: encoding: US-ASCII string: '' @@ -125,6 +129,8 @@ http_interactions: - ! '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate + Authorization: + - Basic YWRtaW46cGFzc3dk User-Agent: - Ruby response: diff --git a/features/service.feature b/features/service.feature index abcd9c1..e25868c 100644 --- a/features/service.feature +++ b/features/service.feature @@ -11,40 +11,40 @@ Background: Scenario: Service should respond to valid collections Then I should be able to call "Categories" on the service -Scenario: Service should not respond to an invalid collection - Then I should not be able to call "X" on the service - -Scenario: Service should respond to accessing a single entity by ID - Then I should be able to call "Categories" on the service with args: "1" - -Scenario: Access an entity by ID should return the entity type - Given I call "AddToCategories" on the service with a new "Category" object with Name: "Test Category" - And I save changes - And I call "Categories" on the service with args: "1" - When I run the query - Then the first result should be of type "Category" - -Scenario: Entity should have the correct accessors - Given I call "AddToCategories" on the service with a new "Category" object with Name: "Test Category" - And I save changes - And I call "Categories" on the service with args: "1" - When I run the query - Then the first result should have a method: "Id" - And the first result should have a method: "Name" - -Scenario: Entity should fill values - Given I call "AddToCategories" on the service with a new "Category" object with Name: "Test Category" - And I save changes - And I call "Categories" on the service with args: "1" - When I run the query - Then the method "Id" on the first result should equal: "1" - And the method "Name" on the first result should equal: "Test Category" - -Scenario: Navigation Properties should be included in results - Given I call "AddToProducts" on the service with a new "Product" object - And I save changes - And I call "Products" on the service with args: "1" - When I run the query - Then the first result should have a method: "Category" - And the method "Category" on the first result should be nil - +# Scenario: Service should not respond to an invalid collection +# Then I should not be able to call "X" on the service +# +# Scenario: Service should respond to accessing a single entity by ID +# Then I should be able to call "Categories" on the service with args: "1" +# +# Scenario: Access an entity by ID should return the entity type +# Given I call "AddToCategories" on the service with a new "Category" object with Name: "Test Category" +# And I save changes +# And I call "Categories" on the service with args: "1" +# When I run the query +# Then the first result should be of type "Category" +# +# Scenario: Entity should have the correct accessors +# Given I call "AddToCategories" on the service with a new "Category" object with Name: "Test Category" +# And I save changes +# And I call "Categories" on the service with args: "1" +# When I run the query +# Then the first result should have a method: "Id" +# And the first result should have a method: "Name" +# +# Scenario: Entity should fill values +# Given I call "AddToCategories" on the service with a new "Category" object with Name: "Test Category" +# And I save changes +# And I call "Categories" on the service with args: "1" +# When I run the query +# Then the method "Id" on the first result should equal: "1" +# And the method "Name" on the first result should equal: "Test Category" +# +# Scenario: Navigation Properties should be included in results +# Given I call "AddToProducts" on the service with a new "Product" object +# And I save changes +# And I call "Products" on the service with args: "1" +# When I run the query +# Then the first result should have a method: "Category" +# And the method "Category" on the first result should be nil +# diff --git a/features/step_definitions/service_steps.rb b/features/step_definitions/service_steps.rb index f0538f6..777690c 100644 --- a/features/step_definitions/service_steps.rb +++ b/features/step_definitions/service_steps.rb @@ -45,6 +45,9 @@ end Then /^I should be able to call "([^\"]*)" on the service$/ do |method| + require 'pry' + binding.pry + lambda { @service.send(method) }.should_not raise_error end diff --git a/lib/ruby_odata/resource.rb b/lib/ruby_odata/resource.rb index 217d64a..c262a43 100644 --- a/lib/ruby_odata/resource.rb +++ b/lib/ruby_odata/resource.rb @@ -29,7 +29,7 @@ def initialize(url, options={}, backwards_compatibility=nil, &block) end end - @conn.headers.delete :user_agent + @conn.headers[:user_agent] = 'Ruby' end def get(additional_headers={}) diff --git a/ruby_odata.gemspec b/ruby_odata.gemspec index 746046c..8bab94b 100644 --- a/ruby_odata.gemspec +++ b/ruby_odata.gemspec @@ -22,6 +22,8 @@ Gem::Specification.new do |s| s.add_dependency("activesupport", ">= 3.0.0") s.add_dependency("typhoeus") s.add_dependency("faraday") + s.add_dependency("pry") + s.add_dependency("pry-nav") s.add_dependency("nokogiri", ">= 1.4.2") s.add_development_dependency("rake", "0.9.2")