diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index 0a04a465f..9e51aae36 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -1,72 +1,72 @@ require 'puppet/network/http/api' module Puppet::Network::HTTP::API::V1 # How we map http methods and the indirection name in the URI # to an indirection method. METHOD_MAP = { "GET" => { :plural => :search, :singular => :find }, "PUT" => { :singular => :save }, "DELETE" => { :singular => :destroy }, "HEAD" => { :singular => :head } } def uri2indirection(http_method, uri, params) environment, indirection, key = uri.split("/", 4)[1..-1] # the first field is always nil because of the leading slash raise ArgumentError, "The environment must be purely alphanumeric, not '#{environment}'" unless environment =~ /^\w+$/ raise ArgumentError, "The indirection name must be purely alphanumeric, not '#{indirection}'" unless indirection =~ /^\w+$/ method = indirection_method(http_method, indirection) params[:environment] = environment raise ArgumentError, "No request key specified in #{uri}" if key == "" or key.nil? key = URI.unescape(key) Puppet::Indirector::Request.new(indirection, method, key, params) end def indirection2uri(request) indirection = request.method == :search ? pluralize(request.indirection_name.to_s) : request.indirection_name.to_s "/#{request.environment.to_s}/#{indirection}/#{request.escaped_key}#{request.query_string}" end def indirection_method(http_method, indirection) raise ArgumentError, "No support for http method #{http_method}" unless METHOD_MAP[http_method] unless method = METHOD_MAP[http_method][plurality(indirection)] raise ArgumentError, "No support for plural #{http_method} operations" end method end def pluralize(indirection) return(indirection == "status" ? "statuses" : indirection + "s") end def plurality(indirection) # NOTE This specific hook for facts is ridiculous, but it's a *many*-line # fix to not need this, and our goal is to move away from the complication # that leads to the fix being too long. return :singular if indirection == "facts" return :singular if indirection == "status" return :plural if indirection == "inventory" - result = (indirection =~ /s$/) ? :plural : :singular + result = (indirection =~ /s$|_search$/) ? :plural : :singular - indirection.sub!(/s$/, '') if result + indirection.sub!(/s$|_search$|es$/, '') result end end diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb index d47fc8d81..e7348312b 100644 --- a/spec/unit/network/http/api/v1_spec.rb +++ b/spec/unit/network/http/api/v1_spec.rb @@ -1,138 +1,150 @@ #!/usr/bin/env ruby Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } require 'puppet/network/http/api/v1' class V1RestApiTester include Puppet::Network::HTTP::API::V1 end describe Puppet::Network::HTTP::API::V1 do before do @tester = V1RestApiTester.new end it "should be able to convert a URI into a request" do @tester.should respond_to(:uri2indirection) end it "should be able to convert a request into a URI" do @tester.should respond_to(:indirection2uri) end describe "when converting a URI into a request" do before do @tester.stubs(:handler).returns "foo" end it "should require the http method, the URI, and the query parameters" do # Not a terribly useful test, but an important statement for the spec lambda { @tester.uri2indirection("/foo") }.should raise_error(ArgumentError) end it "should use the first field of the URI as the environment" do @tester.uri2indirection("GET", "/env/foo/bar", {}).environment.should == Puppet::Node::Environment.new("env") end it "should fail if the environment is not alphanumeric" do lambda { @tester.uri2indirection("GET", "/env ness/foo/bar", {}) }.should raise_error(ArgumentError) end it "should use the environment from the URI even if one is specified in the parameters" do @tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"}).environment.should == Puppet::Node::Environment.new("env") end it "should use the second field of the URI as the indirection name" do @tester.uri2indirection("GET", "/env/foo/bar", {}).indirection_name.should == :foo end it "should fail if the indirection name is not alphanumeric" do lambda { @tester.uri2indirection("GET", "/env/foo ness/bar", {}) }.should raise_error(ArgumentError) end it "should use the remainder of the URI as the indirection key" do @tester.uri2indirection("GET", "/env/foo/bar", {}).key.should == "bar" end it "should support the indirection key being a /-separated file path" do @tester.uri2indirection("GET", "/env/foo/bee/baz/bomb", {}).key.should == "bee/baz/bomb" end it "should fail if no indirection key is specified" do lambda { @tester.uri2indirection("GET", "/env/foo/", {}) }.should raise_error(ArgumentError) lambda { @tester.uri2indirection("GET", "/env/foo", {}) }.should raise_error(ArgumentError) end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is singular" do @tester.uri2indirection("GET", "/env/foo/bar", {}).method.should == :find end it "should choose 'head' as the indirection method if the http method is a HEAD and the indirection name is singular" do @tester.uri2indirection("HEAD", "/env/foo/bar", {}).method.should == :head end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is plural" do @tester.uri2indirection("GET", "/env/foos/bar", {}).method.should == :search end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is facts" do @tester.uri2indirection("GET", "/env/facts/bar", {}).method.should == :find end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is facts" do @tester.uri2indirection("PUT", "/env/facts/bar", {}).method.should == :save end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is inventory" do @tester.uri2indirection("GET", "/env/inventory/search", {}).method.should == :search end + it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is facts_search" do + @tester.uri2indirection("GET", "/env/facts_search/bar", {}).method.should == :search + end + + it "should change indirection name to 'facts' if the http method is a GET and the indirection name is facts_search" do + @tester.uri2indirection("GET", "/env/facts_search/bar", {}).indirection_name.should == :facts + end + + it "should change indirection name to 'status' if the http method is a GEt and the indirection name is statuses" do + @tester.uri2indirection("GET", "/env/statuses/bar", {}).indirection_name.should == :status + end + it "should choose 'delete' as the indirection method if the http method is a DELETE and the indirection name is singular" do @tester.uri2indirection("DELETE", "/env/foo/bar", {}).method.should == :destroy end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is singular" do @tester.uri2indirection("PUT", "/env/foo/bar", {}).method.should == :save end it "should fail if an indirection method cannot be picked" do lambda { @tester.uri2indirection("UPDATE", "/env/foo/bar", {}) }.should raise_error(ArgumentError) end it "should URI unescape the indirection key" do escaped = URI.escape("foo bar") @tester.uri2indirection("GET", "/env/foo/#{escaped}", {}).key.should == "foo bar" end end describe "when converting a request into a URI" do before do @request = Puppet::Indirector::Request.new(:foo, :find, "with spaces", :foo => :bar, :environment => "myenv") end it "should use the environment as the first field of the URI" do @tester.indirection2uri(@request).split("/")[1].should == "myenv" end it "should use the indirection as the second field of the URI" do @tester.indirection2uri(@request).split("/")[2].should == "foo" end it "should pluralize the indirection name if the method is 'search'" do @request.stubs(:method).returns :search @tester.indirection2uri(@request).split("/")[2].should == "foos" end it "should use the escaped key as the remainder of the URI" do escaped = URI.escape("with spaces") @tester.indirection2uri(@request).split("/")[3].sub(/\?.+/, '').should == escaped end it "should add the query string to the URI" do @request.expects(:query_string).returns "?query" @tester.indirection2uri(@request).should =~ /\?query$/ end end end