diff --git a/lib/remote_storage/riak.rb b/lib/remote_storage/riak.rb index 7f5cdd4..c5d336a 100644 --- a/lib/remote_storage/riak.rb +++ b/lib/remote_storage/riak.rb @@ -68,8 +68,9 @@ module RemoteStorage else object.raw_data = data end + directory_index = category == "" ? "/" : category object.indexes.merge!({:user_id_bin => [user], - :directory_bin => [category]}) + :directory_bin => [directory_index]}) object.store create_missing_directory_objects(user, category) @@ -131,6 +132,7 @@ module RemoteStorage end def directory_entries(user, directory) + directory = "/" if directory == "" map_query = <<-EOH function(v){ keys = v.key.split(':'); @@ -150,6 +152,7 @@ module RemoteStorage end def sub_directories(user, directory) + directory = "/" if directory == "" map_query = <<-EOH function(v){ keys = v.key.split(':'); @@ -178,11 +181,17 @@ module RemoteStorage end parent_directories.pop end + + unless directory_bucket.exist?("#{user}:") + update_directory_object(user, "") + end end def update_directory_object(user, category) if category.match /\// parent_directory = category[0..category.rindex("/")-1] + elsif category != "" + parent_directory = "/" end directory = directory_bucket.new("#{user}:#{category}") directory.raw_data = "" diff --git a/liquor-cabinet.rb b/liquor-cabinet.rb index 506cc47..17b164f 100644 --- a/liquor-cabinet.rb +++ b/liquor-cabinet.rb @@ -31,50 +31,58 @@ class LiquorCabinet < Sinatra::Base disable :protection end - ["/:user/*/:key", "/:user/*/"].each do |path| + ["/:user/*/:key", "/:user/:key", "/:user/*/", "/:user/"].each do |path| before path do headers 'Access-Control-Allow-Origin' => '*', 'Access-Control-Allow-Methods' => 'GET, PUT, DELETE', 'Access-Control-Allow-Headers' => 'Authorization, Content-Type, Origin' headers['Access-Control-Allow-Origin'] = env["HTTP_ORIGIN"] if env["HTTP_ORIGIN"] - @user, @directory, @key = params[:user], params[:splat].first, params[:key] + @user, @key = params[:user], params[:key] + @directory = params[:splat] && params[:splat].first || "" + token = env["HTTP_AUTHORIZATION"] ? env["HTTP_AUTHORIZATION"].split(" ")[1] : "" authorize_request(@user, @directory, token) unless request.options? end end - get "/:user/*/:key" do - get_data(@user, @directory, @key) - end - - get "/:user/*/" do - get_directory_listing(@user, @directory) - end - - put "/:user/*/:key" do - data = request.body.read - - if env['CONTENT_TYPE'] == "application/x-www-form-urlencoded" - content_type = "text/plain; charset=utf-8" - else - content_type = env['CONTENT_TYPE'] + ["/:user/*/:key", "/:user/:key"].each do |path| + get path do + get_data(@user, @directory, @key) end - - put_data(@user, @directory, @key, data, content_type) end - delete "/:user/*/:key" do - delete_data(@user, @directory, @key) + ["/:user/*/", "/:user/"].each do |path| + get path do + get_directory_listing(@user, @directory) + end end - options "/:user/*/:key" do - halt 200 + ["/:user/*/:key", "/:user/:key"].each do |path| + put path do + data = request.body.read + + if env['CONTENT_TYPE'] == "application/x-www-form-urlencoded" + content_type = "text/plain; charset=utf-8" + else + content_type = env['CONTENT_TYPE'] + end + + put_data(@user, @directory, @key, data, content_type) + end end - options "/:user/*/" do - halt 200 + ["/:user/*/:key", "/:user/:key"].each do |path| + delete path do + delete_data(@user, @directory, @key) + end + end + + ["/:user/*/:key", "/:user/:key", "/:user/*/", "/:user/"].each do |path| + options path do + halt 200 + end end private diff --git a/spec/directories_spec.rb b/spec/directories_spec.rb index a2ec3e1..bab11fb 100644 --- a/spec/directories_spec.rb +++ b/spec/directories_spec.rb @@ -8,7 +8,7 @@ describe "Directories" do purge_all_buckets auth = auth_bucket.new("jimmy:123") - auth.data = ["documents:r", "tasks:rw"] + auth.data = [":r", "documents:r", "tasks:rw"] auth.store header "Authorization", "Bearer 123" @@ -107,7 +107,7 @@ describe "Directories" do end end - describe "for an empty or absent directory" do + context "for an empty or absent directory" do it "returns an empty listing" do get "/jimmy/documents/notfound/" @@ -115,6 +115,30 @@ describe "Directories" do last_response.body.must_equal "{}" end end + + context "for the root directory" do + before do + auth = auth_bucket.new("jimmy:123") + auth.data = [":rw"] + auth.store + + put "/jimmy/root-1", "Put my root down" + put "/jimmy/root-2", "Back to the roots" + end + + it "lists the containing objects and direct sub-directories" do + get "/jimmy/" + + last_response.status.must_equal 200 + + content = JSON.parse(last_response.body) + content.must_include "root-1" + content.must_include "root-2" + content.must_include "tasks/" + content["tasks/"].to_s.must_match /\d+/ + content["tasks/"].to_s.length.must_be :>=, 10 + end + end end describe "directory object" do @@ -133,6 +157,18 @@ describe "Directories" do object = directory_bucket.get("jimmy:tasks/home") object.indexes["directory_bin"].must_include "tasks" end + + it "creates directory objects for the parent directories" do + put "/jimmy/tasks/home/trash", "take out the trash" + + object = directory_bucket.get("jimmy:tasks") + object.indexes["directory_bin"].must_include "/" + object.last_modified.wont_be_nil + + object = directory_bucket.get("jimmy:") + object.indexes["directory_bin"].must_be_empty + object.last_modified.wont_be_nil + end end context "existing directory object" do @@ -177,6 +213,18 @@ describe "Directories" do last_response.headers["Access-Control-Allow-Headers"].must_equal "Authorization, Content-Type, Origin" end end + + context "root directory" do + it "has CORS headers set" do + options "/jimmy/" + + last_response.status.must_equal 200 + + last_response.headers["Access-Control-Allow-Origin"].must_equal "*" + last_response.headers["Access-Control-Allow-Methods"].must_equal "GET, PUT, DELETE" + last_response.headers["Access-Control-Allow-Headers"].must_equal "Authorization, Content-Type, Origin" + end + end end describe "DELETE file" do diff --git a/spec/permissions_spec.rb b/spec/permissions_spec.rb index 3c8df8f..219028c 100644 --- a/spec/permissions_spec.rb +++ b/spec/permissions_spec.rb @@ -245,6 +245,42 @@ describe "Permissions" do data_bucket.get("jimmy:documents/very/interesting:text") }.must_raise Riak::HTTPFailedRequest end + + context "root directory" do + before do + object = data_bucket.new("jimmy::root") + object.content_type = "text/plain" + object.data = "Back to the roots" + object.store + end + + it "allows GET requests" do + get "/jimmy/root" + + last_response.status.must_equal 200 + last_response.body.must_equal "Back to the roots" + end + + it "allows PUT requests" do + put "/jimmy/1", "Gonna kick it root down" + + # File.open('response.html', 'w') do |f| + # f.write last_response.body + # end + + last_response.status.must_equal 200 + data_bucket.get("jimmy::1").data.must_equal "Gonna kick it root down" + end + + it "allows DELETE requests" do + delete "/jimmy/root" + + last_response.status.must_equal 204 + lambda { + data_bucket.get("jimmy::root") + }.must_raise Riak::HTTPFailedRequest + end + end end describe "read all" do