From 6f35cfaecc85e54d2d9bd0c1e4253a8f6d4dd7f0 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 28 Mar 2012 14:01:41 +0200 Subject: [PATCH] merge with SVN --- RestServer/ActionDispatcherThread.cpp | 2 +- RestServer/api-collection.dox | 2 +- UnitTests/RestDocuments/Gemfile | 4 + UnitTests/RestDocuments/avocadodb.rb | 132 ++++++ .../rest_create-document_spec.rb | 305 +++++++++++++ .../rest_delete-document_spec.rb | 346 ++++++++++++++ .../RestDocuments/rest_read-document_spec.rb | 404 +++++++++++++++++ .../rest_update-document_spec.rb | 375 ++++++++++++++++ UnitTests/RestDocuments/run_tests | 7 + VocBase/query-base.c | 10 +- VocBase/query-cursor.c | 9 +- VocBase/query-execute.c | 6 - VocBase/query-locks.c | 12 - VocBase/query-locks.h | 13 - VocBase/shadow-data.c | 136 +++--- .../system/{aql-cursor.js => api-cursor.js} | 26 +- js/actions/system/api-database.js | 8 +- js/actions/system/api-help.js | 101 ----- .../system/{aql-query.js => api-query.js} | 12 +- js/actions/system/api_collection.js | 388 ---------------- js/actions/system/api_database.js | 261 ----------- js/actions/system/api_simple.js | 423 ------------------ js/actions/system/collections.js | 9 +- js/actions/system/document.js | 127 ------ js/actions/system/documents.js | 8 +- js/actions/system/front-end.js | 126 +----- js/actions/system/key-value.js | 64 +-- js/client/client.js | 314 +------------ js/client/js-client.h | 314 +------------ js/common/bootstrap/errors.js | 2 + js/common/bootstrap/js-errors.h | 2 + 31 files changed, 1743 insertions(+), 2205 deletions(-) create mode 100644 UnitTests/RestDocuments/Gemfile create mode 100644 UnitTests/RestDocuments/avocadodb.rb create mode 100644 UnitTests/RestDocuments/rest_create-document_spec.rb create mode 100644 UnitTests/RestDocuments/rest_delete-document_spec.rb create mode 100644 UnitTests/RestDocuments/rest_read-document_spec.rb create mode 100644 UnitTests/RestDocuments/rest_update-document_spec.rb create mode 100755 UnitTests/RestDocuments/run_tests rename js/actions/system/{aql-cursor.js => api-cursor.js} (83%) delete mode 100644 js/actions/system/api-help.js rename js/actions/system/{aql-query.js => api-query.js} (86%) delete mode 100644 js/actions/system/api_collection.js delete mode 100644 js/actions/system/api_database.js delete mode 100644 js/actions/system/api_simple.js delete mode 100644 js/actions/system/document.js diff --git a/RestServer/ActionDispatcherThread.cpp b/RestServer/ActionDispatcherThread.cpp index 3ea672c6d4..cd2ac79679 100644 --- a/RestServer/ActionDispatcherThread.cpp +++ b/RestServer/ActionDispatcherThread.cpp @@ -143,7 +143,7 @@ void ActionDispatcherThread::tick (bool idle) { _gc += (idle ? 10 : 1); if (_gc > _gcInterval) { - LOG_DEBUG("collecting garbage..."); + LOG_TRACE("collecting garbage..."); while (!v8::V8::IdleNotification()) { } diff --git a/RestServer/api-collection.dox b/RestServer/api-collection.dox index 61eb1307ce..a753e0f492 100644 --- a/RestServer/api-collection.dox +++ b/RestServer/api-collection.dox @@ -62,7 +62,7 @@ /// The basic operations (create, read, update, delete) for documents are mapped /// to the standard HTTP methods (POST, GET, PUT, DELETE). /// -/// @section HttpCollectionResource Address of an Collection +/// @section HttpCollectionResource Address of a Collection //////////////////////////////////////////////////////////// /// /// All collections in AvocadoDB have an unique identifier. This collection diff --git a/UnitTests/RestDocuments/Gemfile b/UnitTests/RestDocuments/Gemfile new file mode 100644 index 0000000000..e46880748e --- /dev/null +++ b/UnitTests/RestDocuments/Gemfile @@ -0,0 +1,4 @@ +source :rubygems + +gem "httparty", "~> 0.8.1" +gem "rspec", "~> 2.8.0" diff --git a/UnitTests/RestDocuments/avocadodb.rb b/UnitTests/RestDocuments/avocadodb.rb new file mode 100644 index 0000000000..349a439c87 --- /dev/null +++ b/UnitTests/RestDocuments/avocadodb.rb @@ -0,0 +1,132 @@ +require 'httparty' + +class AvocadoDB + include HTTParty + + base_uri 'http://localhost:8529' + format :json + +################################################################################ +## create a collection +################################################################################ + + def self.create_collection (name, wait_for_sync = true) + body = "{ \"name\" : \"#{name}\", \"waitForSync\" : #{wait_for_sync} }" + + doc = self.post("/_api/database/collection", :body => body) + + if doc.code == 409 + return doc.parsed_response['id'] + end + + if doc.code != 200 + return nil + end + + return doc.parsed_response['id'] + end + +################################################################################ +## drop a collection +################################################################################ + + def self.drop_collection (name) + # TODO + end + +################################################################################ +## size of a collection +################################################################################ + + def self.size_collection (name) + doc = self.get("/document?collection=#{name}") # TODO use api call + + return doc.parsed_response['documents'].length + end + +################################################################################ +## generate log file +################################################################################ + + def self.log (args) + if args.key?(:output) + logfile = File.new("logs/#{args[:output]}", "a") + else + logfile = File.new("output.log", "a") + end + + method = args[:method] || :get + url = args[:url] + body = args[:body] + headers = args[:headers] + result = args[:result] + response = result.parsed_response + + logfile.puts '-' * 80 + + h_option = "" + h_sep = "" + + if headers + for k in [ "if-match", "if-none-match" ] do + if headers.key?(k) + h_option = h_option + h_sep + "'-H #{k}: #{headers[k]}'" + h_sep = " " + end + end + h_option = h_option + h_sep + end + + if method == :get + logfile.puts "> curl -X GET #{h_option}--dump - http://localhost:8529#{url}" + logfile.puts + elsif method == :head + logfile.puts "> curl -X HEAD #{h_option}--dump - http://localhost:8529#{url}" + logfile.puts + elsif method == :delete + logfile.puts "> curl -X DELETE #{h_option}--dump - http://localhost:8529#{url}" + logfile.puts + elsif method == :post + if body == nil + logfile.puts "> curl -X POST #{h_option}--dump - http://localhost:8529#{url}" + logfile.puts + else + logfile.puts "> curl --data @- -X POST #{h_option}--dump - http://localhost:8529#{url}" + logfile.puts body + logfile.puts + end + elsif method == :put + if body == nil + logfile.puts "> curl -X PUT #{h_option}--dump - http://localhost:8529#{url}" + logfile.puts + else + logfile.puts "> curl --data @- -X PUT #{h_option}--dump - http://localhost:8529#{url}" + logfile.puts body + logfile.puts + end + else + logfile.puts "MISSING" + end + + logfile.puts "HTTP/1.1 #{result.code} #{result.message}" + + if result.headers.key?('content-type') + logfile.puts "content-type: #{result.headers['content-type']}" + end + + if result.headers.key?('location') + logfile.puts "location: #{result.headers['location']}" + end + + if result.headers.key?('etag') + logfile.puts "etag: #{result.headers['etag']}" + end + + if response != nil + logfile.puts + logfile.puts JSON.pretty_generate(response) + end + + logfile.close + end +end diff --git a/UnitTests/RestDocuments/rest_create-document_spec.rb b/UnitTests/RestDocuments/rest_create-document_spec.rb new file mode 100644 index 0000000000..3bccb5c081 --- /dev/null +++ b/UnitTests/RestDocuments/rest_create-document_spec.rb @@ -0,0 +1,305 @@ +require 'rspec' +require './avocadodb.rb' + +describe AvocadoDB do + prefix = "rest_create-document" + + context "creating a document:" do + +################################################################################ +## error handling +################################################################################ + + context "error handling:" do + it "returns an error if url contains a suffix" do + cmd = "/document/123456" + body = "{}" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(601) + doc.parsed_response['code'].should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :post, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-superfluous-suffix") + end + + it "returns an error if collection idenifier is missing" do + cmd = "/document" + body = "{}" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1204) + doc.parsed_response['code'].should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :post, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-missing-cid") + end + + it "returns an error if the collection identifier is unknown" do + cmd = "/document?collection=123456" + body = "{}" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(404) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1203) + doc.parsed_response['code'].should eq(404) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :post, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-unknown-cid") + end + + it "returns an error if the collection name is unknown" do + cmd = "/document?collection=unknown_collection" + body = "{}" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(404) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1203) + doc.parsed_response['code'].should eq(404) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :post, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-unknown-name") + end + + it "returns an error if the JSON body is corrupted" do + cn = "UnitTestsCollectionBasics" + id = AvocadoDB.create_collection(cn) + + id.should be_kind_of(Integer) + id.should_not be_zero + + cmd = "/document?collection=#{id}" + body = "{ 1 : 2 }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(600) + doc.parsed_response['code'].should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :post, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-bad-json") + + AvocadoDB.size_collection(cn).should eq(0) + + AvocadoDB.drop_collection(cn) + end + end + +################################################################################ +## known collection identifier, waitForSync = true +################################################################################ + + context "known collection identifier, waitForSync = true:" do + before do + @cn = "UnitTestsCollectionBasics" + @cid = AvocadoDB.create_collection(@cn) + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "creating a new document" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + + etag = doc.headers['etag'] + etag.should be_kind_of(String) + + location = doc.headers['location'] + location.should be_kind_of(String) + + rev = doc.parsed_response['_rev'] + rev.should be_kind_of(Integer) + + did = doc.parsed_response['_id'] + did.should be_kind_of(String) + + match = /([0-9]*)\/([0-9]*)/.match(did) + + match[1].should eq("#{@cid}") + + etag.should eq("\"#{rev}\"") + location.should eq("/document/#{did}") + + AvocadoDB.log(:method => :post, :url => cmd, :body => body, :result => doc, :output => "#{prefix}") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + end + +################################################################################ +## known collection identifier, waitForSync = false +################################################################################ + + context "known collection identifier, waitForSync = false:" do + before do + @cn = "UnitTestsCollectionUnsynced" + @cid = AvocadoDB.create_collection(@cn, false) + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "creating a new document" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(202) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + + etag = doc.headers['etag'] + etag.should be_kind_of(String) + + location = doc.headers['location'] + location.should be_kind_of(String) + + rev = doc.parsed_response['_rev'] + rev.should be_kind_of(Integer) + + did = doc.parsed_response['_id'] + did.should be_kind_of(String) + + match = /([0-9]*)\/([0-9]*)/.match(did) + + match[1].should eq("#{@cid}") + + etag.should eq("\"#{rev}\"") + location.should eq("/document/#{did}") + + AvocadoDB.log(:method => :post, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-accept") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + end + +################################################################################ +## known collection name +################################################################################ + + context "known collection name:" do + before do + @cn = "UnitTestsCollectionBasics" + @cid = AvocadoDB.create_collection(@cn) + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "creating a new document" do + cmd = "/document?collection=#{@cn}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + + etag = doc.headers['etag'] + etag.should be_kind_of(String) + + location = doc.headers['location'] + location.should be_kind_of(String) + + rev = doc.parsed_response['_rev'] + rev.should be_kind_of(Integer) + + did = doc.parsed_response['_id'] + did.should be_kind_of(String) + + match = /([0-9]*)\/([0-9]*)/.match(did) + + match[1].should eq("#{@cid}") + + etag.should eq("\"#{rev}\"") + location.should eq("/document/#{did}") + + AvocadoDB.log(:method => :post, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-named-collection") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + end + +################################################################################ +## unknown collection name +################################################################################ + + context "unknown collection name:" do + before do + @cn = "UnitTestsCollectionNamed#{Time.now.to_i}" + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "returns an error if collection is unknown" do + cmd = "/document?collection=#{@cn}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(404) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1203) + doc.parsed_response['code'].should eq(404) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :post, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-unknown-collection-name") + end + + it "create the collection and the document" do + cmd = "/document?collection=#{@cn}&createCollection=true" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(202) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + + etag = doc.headers['etag'] + etag.should be_kind_of(String) + + location = doc.headers['location'] + location.should be_kind_of(String) + + rev = doc.parsed_response['_rev'] + rev.should be_kind_of(Integer) + + did = doc.parsed_response['_id'] + did.should be_kind_of(String) + + etag.should eq("\"#{rev}\"") + location.should eq("/document/#{did}") + + AvocadoDB.log(:method => :post, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-create-collection") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cn).should eq(0) + end + end + + end +end diff --git a/UnitTests/RestDocuments/rest_delete-document_spec.rb b/UnitTests/RestDocuments/rest_delete-document_spec.rb new file mode 100644 index 0000000000..4f6a160696 --- /dev/null +++ b/UnitTests/RestDocuments/rest_delete-document_spec.rb @@ -0,0 +1,346 @@ +require 'rspec' +require './avocadodb.rb' + +describe AvocadoDB do + prefix = "rest_delete-document" + + context "delete a document:" do + +################################################################################ +## error handling +################################################################################ + + context "error handling:" do + before do + @cn = "UnitTestsCollectionBasics" + @cid = AvocadoDB.create_collection(@cn) + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "returns an error if document handle is missing" do + cmd = "/document" + doc = AvocadoDB.delete(cmd) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(400) + doc.parsed_response['code'].should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :delete, :url => cmd, :result => doc, :output => "#{prefix}-missing-handle") + end + + it "returns an error if document handle is corrupted" do + cmd = "/document/123456" + doc = AvocadoDB.delete(cmd) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(400) + doc.parsed_response['code'].should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :delete, :url => cmd, :result => doc, :output => "#{prefix}-bad-handle") + end + + it "returns an error if document handle is corrupted" do + cmd = "/document//123456" + doc = AvocadoDB.delete(cmd) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(600) + doc.parsed_response['code'].should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :delete, :url => cmd, :result => doc, :output => "#{prefix}-bad-handle2") + end + + it "returns an error if collection identifier is unknown" do + cmd = "/document/123456/234567" + doc = AvocadoDB.delete(cmd) + + doc.code.should eq(404) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1203) + doc.parsed_response['code'].should eq(404) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :delete, :url => cmd, :result => doc, :output => "#{prefix}-unknown-cid") + end + + it "returns an error if document handle is unknown" do + cmd = "/document/#{@cid}/234567" + doc = AvocadoDB.delete(cmd) + + doc.code.should eq(404) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1202) + doc.parsed_response['code'].should eq(404) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :delete, :url => cmd, :result => doc, :output => "#{prefix}-unknown-handle") + end + + it "returns an error if the policy parameter is bad" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # delete document, different revision + cmd = "/document/#{did}?policy=last-write" + hdr = { "if-match" => "\"#{rev-1}\"" } + doc = AvocadoDB.delete(cmd, :headers => hdr) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :delete, :url => cmd, :headers => hdr, :result => doc, :output => "#{prefix}-policy-bad") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + end + +################################################################################ +## deleting documents +################################################################################ + + context "deleting documents:" do + before do + @cn = "UnitTestsCollectionBasics" + @cid = AvocadoDB.create_collection(@cn) + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "create a document and delete it" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # delete document + cmd = "/document/#{did}" + doc = AvocadoDB.delete(cmd) + + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + AvocadoDB.log(:method => :delete, :url => cmd, :result => doc, :output => "#{prefix}") + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create a document and delete it, using if-match" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # delete document, different revision + cmd = "/document/#{did}" + hdr = { "if-match" => "\"#{rev-1}\"" } + doc = AvocadoDB.delete(cmd, :headers => hdr) + + doc.code.should eq(412) + doc.parsed_response['error'].should eq(true) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + AvocadoDB.log(:method => :delete, :url => cmd, :headers => hdr, :result => doc, :output => "#{prefix}-if-match-other") + + # delete document, same revision + cmd = "/document/#{did}" + hdr = { "if-match" => "\"#{rev}\"" } + doc = AvocadoDB.delete(cmd, :headers => hdr) + + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + AvocadoDB.log(:method => :delete, :url => cmd, :headers => hdr, :result => doc, :output => "#{prefix}-if-match") + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create a document and delete it, using if-match and last-write wins" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # delete document, different revision + cmd = "/document/#{did}?policy=last" + hdr = { "if-match" => "\"#{rev-1}\"" } + doc = AvocadoDB.delete(cmd, :headers => hdr) + + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + AvocadoDB.log(:method => :delete, :url => cmd, :headers => hdr, :result => doc, :output => "#{prefix}-if-match-other-last-write") + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create a document and delete it, using rev" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # delete document, different revision + cmd = "/document/#{did}?rev=#{rev-1}" + doc = AvocadoDB.delete(cmd) + + doc.code.should eq(412) + doc.parsed_response['error'].should eq(true) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + AvocadoDB.log(:method => :delete, :url => cmd, :result => doc, :output => "#{prefix}-rev-other") + + # delete document, same revision + cmd = "/document/#{did}?rev=#{rev}" + doc = AvocadoDB.delete(cmd) + + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + AvocadoDB.log(:method => :delete, :url => cmd, :result => doc, :output => "#{prefix}-rev") + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create a document and delete it, using rev and last-write wins" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # delete document, different revision + cmd = "/document/#{did}?policy=last" + hdr = { "rev" => "\"#{rev-1}\"" } + doc = AvocadoDB.delete(cmd, :headers => hdr) + + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + AvocadoDB.log(:method => :delete, :url => cmd, :headers => hdr, :result => doc, :output => "#{prefix}-rev-other-last-write") + + AvocadoDB.size_collection(@cid).should eq(0) + end + end + + end +end + diff --git a/UnitTests/RestDocuments/rest_read-document_spec.rb b/UnitTests/RestDocuments/rest_read-document_spec.rb new file mode 100644 index 0000000000..cc399273a1 --- /dev/null +++ b/UnitTests/RestDocuments/rest_read-document_spec.rb @@ -0,0 +1,404 @@ +require 'rspec' +require './avocadodb.rb' + +describe AvocadoDB do + prefix = "rest_read-document" + + context "reading a document:" do + +################################################################################ +## error handling +################################################################################ + + context "error handling:" do + before do + @cn = "UnitTestsCollectionBasics" + @cid = AvocadoDB.create_collection(@cn) + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "returns an error if document handle is corrupted" do + cmd = "/document/123456" + doc = AvocadoDB.get(cmd) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(601) + doc.parsed_response['code'].should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :get, :url => cmd, :result => doc, :output => "#{prefix}-bad-handle") + end + + it "returns an error if document handle is corrupted" do + cmd = "/document//123456" + doc = AvocadoDB.get(cmd) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(600) + doc.parsed_response['code'].should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :get, :url => cmd, :result => doc, :output => "#{prefix}-bad-handle2") + end + + it "returns an error if collection identifier is unknown" do + cmd = "/document/123456/234567" + doc = AvocadoDB.get(cmd) + + doc.code.should eq(404) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1203) + doc.parsed_response['code'].should eq(404) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :get, :url => cmd, :result => doc, :output => "#{prefix}-unknown-cid") + end + + it "returns an error if document handle is unknown" do + cmd = "/document/#{@cid}/234567" + doc = AvocadoDB.get(cmd) + + doc.code.should eq(404) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1202) + doc.parsed_response['code'].should eq(404) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :get, :url => cmd, :result => doc, :output => "#{prefix}-unknown-handle") + + AvocadoDB.size_collection(@cid).should eq(0) + end + end + +################################################################################ +## reading documents +################################################################################ + + context "reading a document:" do + before do + @cn = "UnitTestsCollectionBasics" + @cid = AvocadoDB.create_collection(@cn) + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "create a document and read it" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # get document + cmd = "/document/#{did}" + doc = AvocadoDB.get(cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + etag = doc.headers['etag'] + etag.should be_kind_of(String) + + etag.should eq("\"#{rev}\"") + + AvocadoDB.log(:method => :get, :url => cmd, :result => doc, :output => "#{prefix}") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create a document and read it, use if-none-match" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # get document, if-none-match with same rev + cmd = "/document/#{did}" + hdr = { "if-none-match" => "\"#{rev}\"" } + doc = AvocadoDB.get(cmd, :headers => hdr) + + doc.code.should eq(304) + + etag = doc.headers['etag'] + etag.should be_kind_of(String) + + etag.should eq("\"#{rev}\"") + + AvocadoDB.log(:method => :get, :url => cmd, :headers => hdr, :result => doc, :output => "#{prefix}-if-none-match") + + # get document, if-none-match with different rev + cmd = "/document/#{did}" + hdr = { "if-none-match" => "\"#{rev-1}\"" } + doc = AvocadoDB.get(cmd, :headers => hdr) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + etag = doc.headers['etag'] + etag.should be_kind_of(String) + + etag.should eq("\"#{rev}\"") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + etag = doc.headers['etag'] + etag.should be_kind_of(String) + + etag.should eq("\"#{rev}\"") + + AvocadoDB.log(:method => :get, :url => cmd, :headers => hdr, :result => doc, :output => "#{prefix}-if-none-match-other") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create a document and read it, use if-match" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # get document, if-match with same rev + cmd = "/document/#{did}" + hdr = { "if-match" => "\"#{rev}\"" } + doc = AvocadoDB.get(cmd, :headers => hdr) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + etag = doc.headers['etag'] + etag.should be_kind_of(String) + + etag.should eq("\"#{rev}\"") + + AvocadoDB.log(:method => :get, :url => cmd, :headers => hdr, :result => doc, :output => "#{prefix}-if-match") + + # get document, if-match with different rev + cmd = "/document/#{did}" + hdr = { "if-match" => "\"#{rev-1}\"" } + doc = AvocadoDB.get(cmd, :headers => hdr) + + doc.code.should eq(412) + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + AvocadoDB.log(:method => :get, :url => cmd, :headers => hdr, :result => doc, :output => "#{prefix}-if-match-other") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + end + +################################################################################ +## reading all documents +################################################################################ + + context "reading all documents:" do + before do + @cn = "UnitTestsCollectionAll" + @cid = AvocadoDB.create_collection(@cn) + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "get all documents of an empty collection" do + cmd = "/document?collection=#{@cid}" + + # get documents + cmd = "/document?collection=#{@cid}" + doc = AvocadoDB.get(cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + documents = doc.parsed_response['documents'] + documents.should be_kind_of(Array) + documents.length.should eq(0) + + AvocadoDB.log(:method => :get, :url => cmd, :result => doc, :output => "#{prefix}-all-0") + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create three documents and read them using the collection identifier" do + cmd = "/document?collection=#{@cid}" + + location = [] + + for i in [ 1, 2, 3 ] + body = "{ \"Hallo\" : \"World-#{i}\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location.push(doc.headers['location']) + end + + # get document + cmd = "/document?collection=#{@cid}" + doc = AvocadoDB.get(cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + documents = doc.parsed_response['documents'] + documents.should be_kind_of(Array) + documents.length.should eq(3) + + AvocadoDB.log(:method => :get, :url => cmd, :result => doc, :output => "#{prefix}-all") + + for l in location + AvocadoDB.delete(l) + end + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create three documents and read them using the collection name" do + cmd = "/document?collection=#{@cn}" + + location = [] + + for i in [ 1, 2, 3 ] + body = "{ \"Hallo\" : \"World-#{i}\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location.push(doc.headers['location']) + end + + # get document + cmd = "/document?collection=#{@cn}" + doc = AvocadoDB.get(cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + documents = doc.parsed_response['documents'] + documents.should be_kind_of(Array) + documents.length.should eq(3) + + AvocadoDB.log(:method => :get, :url => cmd, :result => doc, :output => "#{prefix}-all-name") + + for l in location + AvocadoDB.delete(l) + end + + AvocadoDB.size_collection(@cid).should eq(0) + end + end + +################################################################################ +## checking document +################################################################################ + + context "checking a document:" do + before do + @cn = "UnitTestsCollectionBasics" + @cid = AvocadoDB.create_collection(@cn) + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "create a document and read it" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + # get document + cmd = location + doc = AvocadoDB.get(cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + content_length = doc.headers['content-length'] + + # get the document head + doc = AvocadoDB.head(cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + doc.headers['content-length'].should eq(content_length) + doc.body.should eq(nil) + + AvocadoDB.log(:method => :head, :url => cmd, :result => doc, :output => "#{prefix}-head") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + end + + end +end diff --git a/UnitTests/RestDocuments/rest_update-document_spec.rb b/UnitTests/RestDocuments/rest_update-document_spec.rb new file mode 100644 index 0000000000..180d296baa --- /dev/null +++ b/UnitTests/RestDocuments/rest_update-document_spec.rb @@ -0,0 +1,375 @@ +require 'rspec' +require './avocadodb.rb' + +describe AvocadoDB do + prefix = "rest_update-document" + + context "update a document:" do + +################################################################################ +## error handling +################################################################################ + + context "error handling:" do + before do + @cn = "UnitTestsCollectionBasics" + @cid = AvocadoDB.create_collection(@cn) + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "returns an error if document handle is missing" do + cmd = "/document" + body = "{}" + doc = AvocadoDB.put(cmd, :body => body) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(400) + doc.parsed_response['code'].should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-missing-handle") + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "returns an error if document handle is corrupted" do + cmd = "/document/123456" + body = "{}" + doc = AvocadoDB.put(cmd, :body => body) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(400) + doc.parsed_response['code'].should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-bad-handle") + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "returns an error if document handle is corrupted" do + cmd = "/document//123456" + body = "{}" + doc = AvocadoDB.put(cmd, :body => body) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(600) + doc.parsed_response['code'].should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-bad-handle2") + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "returns an error if collection identifier is unknown" do + cmd = "/document/123456/234567" + body = "{}" + doc = AvocadoDB.put(cmd, :body => body) + + doc.code.should eq(404) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1203) + doc.parsed_response['code'].should eq(404) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-unknown-cid") + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "returns an error if document handle is unknown" do + cmd = "/document/#{@cid}/234567" + body = "{}" + doc = AvocadoDB.put(cmd, :body => body) + + doc.code.should eq(404) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1202) + doc.parsed_response['code'].should eq(404) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-unknown-handle") + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "returns an error if the policy parameter is bad" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # update document, different revision + cmd = "/document/#{did}?policy=last-write" + hdr = { "if-match" => "\"#{rev-1}\"" } + doc = AvocadoDB.put(cmd, :headers => hdr) + + doc.code.should eq(400) + doc.parsed_response['error'].should eq(true) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + AvocadoDB.log(:method => :put, :url => cmd, :headers => hdr, :result => doc, :output => "#{prefix}-policy-bad") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + end + +################################################################################ +## updating documents +################################################################################ + + context "updating document:" do + before do + @cn = "UnitTestsCollectionBasics" + @cid = AvocadoDB.create_collection(@cn) + end + + after do + AvocadoDB.drop_collection(@cn) + end + + it "create a document and update it" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # update document + cmd = "/document/#{did}" + body = "{ \"World\" : \"Hallo\" }" + doc = AvocadoDB.put(cmd, :body => body) + + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should_not eq(rev) + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :result => doc, :output => "#{prefix}") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create a document and update it, using if-match" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # update document, different revision + cmd = "/document/#{did}" + hdr = { "if-match" => "\"#{rev-1}\"" } + body = "{ \"World\" : \"Hallo\" }" + doc = AvocadoDB.put(cmd, :headers => hdr, :body => body) + + doc.code.should eq(412) + doc.parsed_response['error'].should eq(true) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :headers => hdr, :result => doc, :output => "#{prefix}-if-match-other") + + # update document, same revision + cmd = "/document/#{did}" + hdr = { "if-match" => "\"#{rev}\"" } + body = "{ \"World\" : \"Hallo\" }" + doc = AvocadoDB.put(cmd, :headers => hdr, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should_not eq(rev) + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :headers => hdr, :result => doc, :output => "#{prefix}-if-match") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create a document and update it, using if-match and last-write wins" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # update document, different revision + cmd = "/document/#{did}?policy=last" + hdr = { "if-match" => "\"#{rev-1}\"" } + body = "{ \"World\" : \"Hallo\" }" + doc = AvocadoDB.put(cmd, :headers => hdr, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should_not eq(rev) + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :headers => hdr, :result => doc, :output => "#{prefix}-if-match-other-last-write") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create a document and update it, using rev" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # update document, different revision + cmd = "/document/#{did}?rev=#{rev-1}" + body = "{ \"World\" : \"Hallo\" }" + doc = AvocadoDB.put(cmd, :body => body) + + doc.code.should eq(412) + doc.parsed_response['error'].should eq(true) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should eq(rev) + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-rev-other") + + # update document, same revision + cmd = "/document/#{did}?rev=#{rev}" + body = "{ \"World\" : \"Hallo\" }" + doc = AvocadoDB.put(cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should_not eq(rev) + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-rev") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + + it "create a document and update it, using rev and last-write wins" do + cmd = "/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + doc = AvocadoDB.post(cmd, :body => body) + + doc.code.should eq(201) + + location = doc.headers['location'] + location.should be_kind_of(String) + + did = doc.parsed_response['_id'] + rev = doc.parsed_response['_rev'] + + # update document, different revision + cmd = "/document/#{did}?policy=last&rev=#{rev-1}" + body = "{ \"World\" : \"Hallo\" }" + doc = AvocadoDB.put(cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + + did2 = doc.parsed_response['_id'] + did2.should be_kind_of(String) + did2.should eq(did) + + rev2 = doc.parsed_response['_rev'] + rev2.should be_kind_of(Integer) + rev2.should_not eq(rev) + + AvocadoDB.log(:method => :put, :url => cmd, :body => body, :result => doc, :output => "#{prefix}-rev-other-last-write") + + AvocadoDB.delete(location) + + AvocadoDB.size_collection(@cid).should eq(0) + end + end + + end +end diff --git a/UnitTests/RestDocuments/run_tests b/UnitTests/RestDocuments/run_tests new file mode 100755 index 0000000000..2303cd922f --- /dev/null +++ b/UnitTests/RestDocuments/run_tests @@ -0,0 +1,7 @@ +#!/bin/sh +test -d logs || mkdir logs + +rspec rest_create-document_spec.rb --format d +rspec rest_read-document_spec.rb --format d +rspec rest_update-document_spec.rb --format d +rspec rest_delete-document_spec.rb --format d diff --git a/VocBase/query-base.c b/VocBase/query-base.c index 97938c6de2..d2e87b05a8 100644 --- a/VocBase/query-base.c +++ b/VocBase/query-base.c @@ -1121,9 +1121,6 @@ static void FreeJoinsQueryInstance (TRI_query_instance_t* const instance) { static bool AddBindParameterValues (TRI_query_instance_t* const instance, const TRI_json_t* parameters) { - TRI_bind_parameter_t* parameter; - void* nameParameter; - void* valueParameter; size_t i; assert(parameters); @@ -1136,6 +1133,10 @@ static bool AddBindParameterValues (TRI_query_instance_t* const instance, } for (i = 0; i < parameters->_value._objects._length; i += 2) { + void* nameParameter; + void* valueParameter; + TRI_bind_parameter_t* parameter; + nameParameter = TRI_AtVector(¶meters->_value._objects, i); valueParameter = TRI_AtVector(¶meters->_value._objects, i + 1); @@ -1181,7 +1182,6 @@ static bool AddBindParameterValues (TRI_query_instance_t* const instance, static bool ValidateBindParameters (TRI_query_instance_t* const instance) { TRI_associative_pointer_t* templateParameters; TRI_associative_pointer_t* instanceParameters; - TRI_bind_parameter_t* parameter; size_t i; templateParameters = &instance->_template->_bindParameters; @@ -1189,6 +1189,8 @@ static bool ValidateBindParameters (TRI_query_instance_t* const instance) { // enumerate all template bind parameters.... for (i = 0; i < templateParameters->_nrAlloc; i++) { + TRI_bind_parameter_t* parameter; + parameter = (TRI_bind_parameter_t*) templateParameters->_table[i]; if (!parameter) { continue; diff --git a/VocBase/query-cursor.c b/VocBase/query-cursor.c index e93da884d9..6e64b15005 100644 --- a/VocBase/query-cursor.c +++ b/VocBase/query-cursor.c @@ -29,7 +29,6 @@ #include "VocBase/query-cursor.h" #include "VocBase/query-context.h" -#include "VocBase/query-locks.h" // ----------------------------------------------------------------------------- // --SECTION-- private functions @@ -143,14 +142,12 @@ static uint32_t GetBatchSizeQueryCursor (const TRI_query_cursor_t* const cursor) void TRI_FreeQueryCursor (TRI_query_cursor_t* cursor) { FreeData(cursor); - if (cursor->_locks) { - TRI_FreeLocksQueryInstance(cursor->_vocbase, cursor->_locks); - } - TRI_DestroyMutex(&cursor->_lock); TRI_DestroyVectorPointer(&cursor->_containers); TRI_Free(cursor); + + LOG_DEBUG("destroyed query cursor"); } @@ -201,6 +198,8 @@ TRI_query_cursor_t* TRI_CreateQueryCursor (TRI_query_instance_t* const instance, TRI_InitMutex(&cursor->_lock); TRI_InitVectorPointer(&cursor->_containers); + + LOG_DEBUG("created query cursor"); return cursor; } diff --git a/VocBase/query-execute.c b/VocBase/query-execute.c index 0304809ccc..aa9928f0ab 100644 --- a/VocBase/query-execute.c +++ b/VocBase/query-execute.c @@ -170,12 +170,6 @@ TRI_query_cursor_t* TRI_ExecuteQueryInstance (TRI_query_instance_t* const instan cursor->_length = selectResult->_numRows; cursor->_currentRow = 0; - if (cursor->_length > 0 && !instance->_query._select._isConstant) { - // we have a result set. the cursor now becomes responsible - // for freeing any locks we still have on the underlying collections - TRI_HandoverLocksQueryInstance(instance, cursor); - } - return cursor; } diff --git a/VocBase/query-locks.c b/VocBase/query-locks.c index 5651aa15bf..a985879a5f 100644 --- a/VocBase/query-locks.c +++ b/VocBase/query-locks.c @@ -256,18 +256,6 @@ bool TRI_AddCollectionsBarrierQueryInstance (TRI_query_instance_t* const instanc return true; } - -//////////////////////////////////////////////////////////////////////////////// -/// @brief hand over locks from query instance to result cursor -//////////////////////////////////////////////////////////////////////////////// - -void TRI_HandoverLocksQueryInstance (TRI_query_instance_t* const instance, - TRI_query_cursor_t* const cursor) { - cursor->_locks = instance->_locks; - instance->_locks = NULL; - // cursor is now responsible for freeing the locks -} - //////////////////////////////////////////////////////////////////////////////// /// @brief create a vector for holding collection locks //////////////////////////////////////////////////////////////////////////////// diff --git a/VocBase/query-locks.h b/VocBase/query-locks.h index 2434c22fc6..76d9e17a45 100644 --- a/VocBase/query-locks.h +++ b/VocBase/query-locks.h @@ -109,19 +109,6 @@ void TRI_ReadUnlockCollectionsQueryInstance (TRI_query_instance_t* const); bool TRI_AddCollectionsBarrierQueryInstance (TRI_query_instance_t* const, TRI_query_cursor_t* const); -//////////////////////////////////////////////////////////////////////////////// -/// @brief hand over locks from query instance to result cursor -/// -/// This function is called when there is a select result with at least one row. -/// The query instance will be freed immediately after executing the select, -/// but the result set cursor might still be in use. The underlying collections -/// are still needed and are still read-locked. When the cursor usage is over, -/// the cursor is responsible for freeing the locks held. -//////////////////////////////////////////////////////////////////////////////// - -void TRI_HandoverLocksQueryInstance (TRI_query_instance_t* const, - TRI_query_cursor_t* const); - //////////////////////////////////////////////////////////////////////////////// /// @brief create a vector for holding collection locks //////////////////////////////////////////////////////////////////////////////// diff --git a/VocBase/shadow-data.c b/VocBase/shadow-data.c index 2e05928548..21dfdfe596 100644 --- a/VocBase/shadow-data.c +++ b/VocBase/shadow-data.c @@ -77,6 +77,68 @@ static TRI_shadow_t* CreateShadow (const void* const data) { return shadow; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief decrease the refcount for a shadow +//////////////////////////////////////////////////////////////////////////////// + +static void DecreaseRefCount (TRI_shadow_store_t* const store, TRI_shadow_t* const shadow) { + LOG_TRACE("decreasing refcount for shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + if (--shadow->_rc <= 0 && shadow->_type == SHADOW_TRANSIENT) { + LOG_TRACE("deleting shadow %p", shadow); + + TRI_RemoveKeyAssociativePointer(&store->_ids, &shadow->_id); + TRI_RemoveKeyAssociativePointer(&store->_pointers, shadow->_data); + store->destroyShadow(shadow->_data); + TRI_Free(shadow); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief increase the refcount for a shadow +//////////////////////////////////////////////////////////////////////////////// + +static void IncreaseRefCount (TRI_shadow_store_t* const store, TRI_shadow_t* const shadow) { + LOG_TRACE("increasing refcount for shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + ++shadow->_rc; + UpdateTimestampShadow(shadow); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the persistence flag for a shadow +//////////////////////////////////////////////////////////////////////////////// + +static void PersistShadow (TRI_shadow_t* const shadow) { + LOG_TRACE("persisting shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + shadow->_type = SHADOW_PERSISTENT; + UpdateTimestampShadow(shadow); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the deleted flag for a shadow +//////////////////////////////////////////////////////////////////////////////// + +static void DeleteShadow (TRI_shadow_store_t* const store, TRI_shadow_t* const shadow) { + LOG_TRACE("setting deleted flag for shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + shadow->_deleted = true; + DecreaseRefCount(store, shadow); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief hashes an element in the ids index //////////////////////////////////////////////////////////////////////////////// @@ -260,13 +322,7 @@ void* TRI_BeginUsageDataShadowData (TRI_shadow_store_t* const store, shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_pointers, data); if (shadow && !shadow->_deleted) { - LOG_TRACE("increasing refcount for shadow %p with data ptr %p and id %lu", - shadow, - shadow->_data, - (unsigned long) shadow->_id); - - ++shadow->_rc; - UpdateTimestampShadow(shadow); + IncreaseRefCount(store, shadow); TRI_UnlockMutex(&store->_lock); return shadow->_data; } @@ -292,13 +348,7 @@ void* TRI_BeginUsageIdShadowData (TRI_shadow_store_t* const store, shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_ids, (void const*) &id); if (shadow && !shadow->_deleted) { - LOG_TRACE("increasing refcount for shadow %p with data ptr %p and id %lu", - shadow, - shadow->_data, - (unsigned long) shadow->_id); - - ++shadow->_rc; - UpdateTimestampShadow(shadow); + IncreaseRefCount(store, shadow); TRI_UnlockMutex(&store->_lock); return shadow->_data; } @@ -325,19 +375,7 @@ void TRI_EndUsageDataShadowData (TRI_shadow_store_t* const store, shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_pointers, data); if (shadow && !shadow->_deleted) { - LOG_TRACE("decreasing refcount for shadow %p with data ptr %p and id %lu", - shadow, - shadow->_data, - (unsigned long) shadow->_id); - - if (--shadow->_rc <= 0 && shadow->_type == SHADOW_TRANSIENT) { - LOG_TRACE("deleting shadow %p", shadow); - - TRI_RemoveKeyAssociativePointer(&store->_ids, &shadow->_id); - TRI_RemoveKeyAssociativePointer(&store->_pointers, data); - store->destroyShadow(shadow->_data); - TRI_Free(shadow); - } + DecreaseRefCount(store, shadow); // this might delete the shadow } TRI_UnlockMutex(&store->_lock); @@ -361,19 +399,7 @@ void TRI_EndUsageIdShadowData (TRI_shadow_store_t* const store, shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_ids, &id); if (shadow && !shadow->_deleted) { - LOG_TRACE("decreasing refcount for shadow %p with data ptr %p and id %lu", - shadow, - shadow->_data, - (unsigned long) shadow->_id); - - if (--shadow->_rc <= 0 && shadow->_type == SHADOW_TRANSIENT) { - LOG_TRACE("deleting shadow %p", shadow); - - TRI_RemoveKeyAssociativePointer(&store->_ids, &id); - TRI_RemoveKeyAssociativePointer(&store->_pointers, shadow->_data); - store->destroyShadow(shadow->_data); - TRI_Free(shadow); - } + DecreaseRefCount(store, shadow); // this might delete the shadow } TRI_UnlockMutex(&store->_lock); @@ -394,13 +420,7 @@ bool TRI_PersistDataShadowData (TRI_shadow_store_t* const store, shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_pointers, data); if (shadow && !shadow->_deleted) { - LOG_TRACE("persisting shadow %p with data ptr %p and id %lu", - shadow, - shadow->_data, - (unsigned long) shadow->_id); - - shadow->_type = SHADOW_PERSISTENT; - UpdateTimestampShadow(shadow); + PersistShadow(shadow); result = true; } @@ -424,13 +444,7 @@ bool TRI_PersistIdShadowData (TRI_shadow_store_t* const store, shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_ids, &id); if (shadow && !shadow->_deleted) { - LOG_TRACE("persisting shadow %p with data ptr %p and id %lu", - shadow, - shadow->_data, - (unsigned long) shadow->_id); - - shadow->_type = SHADOW_PERSISTENT; - UpdateTimestampShadow(shadow); + PersistShadow(shadow); result = true; } @@ -455,12 +469,7 @@ bool TRI_DeleteDataShadowData (TRI_shadow_store_t* const store, shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_pointers, data); if (shadow && !shadow->_deleted) { - LOG_TRACE("setting deleted flag for shadow %p with data ptr %p and id %lu", - shadow, - shadow->_data, - (unsigned long) shadow->_id); - - shadow->_deleted = true; + DeleteShadow(store, shadow); found = true; } @@ -485,12 +494,7 @@ bool TRI_DeleteIdShadowData (TRI_shadow_store_t* const store, shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_ids, &id); if (shadow && !shadow->_deleted) { - LOG_TRACE("setting deleted flag for shadow %p with data ptr %p and id %lu", - shadow, - shadow->_data, - (unsigned long) shadow->_id); - - shadow->_deleted = true; + DeleteShadow(store, shadow); found = true; } diff --git a/js/actions/system/aql-cursor.js b/js/actions/system/api-cursor.js similarity index 83% rename from js/actions/system/aql-cursor.js rename to js/actions/system/api-cursor.js index 8a29a1c36c..eb35abd46c 100644 --- a/js/actions/system/aql-cursor.js +++ b/js/actions/system/api-cursor.js @@ -84,7 +84,7 @@ function getCursorResult(cursor) { function postCursor(req, res) { if (req.suffix.length != 0) { - actions.actionResultError (req, res, 404, actions.errorInvalidRequest, "Invalid request"); + actions.resultError (req, res, 404, actions.errorInvalidRequest, "Invalid request"); return; } @@ -92,7 +92,7 @@ function postCursor(req, res) { var json = JSON.parse(req.requestBody); if (!json || !(json instanceof Object)) { - actions.actionResultError (req, res, 400, actions.errorQuerySpecificationInvalid, "Query specification invalid"); + actions.resultError (req, res, 400, actions.errorQuerySpecificationInvalid, "Query specification invalid"); return; } @@ -104,23 +104,23 @@ function postCursor(req, res) { (json.batchSize != undefined ? json.batchSize : 1000)); } else { - actions.actionResultError (req, res, 400, actions.errorQuerySpecificationInvalid, "Query specification invalid"); + actions.resultError (req, res, 400, actions.errorQuerySpecificationInvalid, "Query specification invalid"); return; } if (cursor instanceof AvocadoQueryError) { // error occurred - actions.actionResultError (req, res, 404, cursor.code, cursor.message); + actions.resultError (req, res, 404, cursor.code, cursor.message); return; } // this might dispose or persist the cursor var result = getCursorResult(cursor); - actions.actionResultOK(req, res, 201, result); + actions.resultOk(req, res, 201, result); } catch (e) { - actions.actionResultError (req, res, 404, actions.errorJavascriptException, "Javascript exception"); + actions.resultError (req, res, 404, actions.errorJavascriptException, "Javascript exception"); } } @@ -130,7 +130,7 @@ function postCursor(req, res) { function putCursor(req, res) { if (req.suffix.length != 1) { - actions.actionResultError (req, res, 404, actions.errorInvalidRequest, "Invalid request"); + actions.resultError (req, res, 404, actions.errorInvalidRequest, "Invalid request"); return; } @@ -142,10 +142,10 @@ function putCursor(req, res) { } // note: this might dispose or persist the cursor - actions.actionResultOK(req, res, 200, getCursorResult(cursor)); + actions.resultOk(req, res, 200, getCursorResult(cursor)); } catch (e) { - actions.actionResultError (req, res, 404, actions.errorCursorNotFound, "Cursor not found"); + actions.resultError (req, res, 404, actions.errorCursorNotFound, "Cursor not found"); } } @@ -155,7 +155,7 @@ function putCursor(req, res) { function deleteCursor(req, res) { if (req.suffix.length != 1) { - actions.actionResultError (req, res, 404, actions.errorInvalidRequest, "Invalid request"); + actions.resultError (req, res, 404, actions.errorInvalidRequest, "Invalid request"); return; } @@ -167,10 +167,10 @@ function deleteCursor(req, res) { } cursor.dispose(); - actions.actionResultOK(req, res, 202, { "_id" : cursorId }); + actions.resultOk(req, res, 202, { "_id" : cursorId }); } catch (e) { - actions.actionResultError (req, res, 404, actions.errorCursorNotFound, "Cursor not found"); + actions.resultError (req, res, 404, actions.errorCursorNotFound, "Cursor not found"); } } @@ -201,7 +201,7 @@ actions.defineHttp({ break; default: - actions.actionResultUnsupported(req, res); + actions.resultUnsupported(req, res); } } }); diff --git a/js/actions/system/api-database.js b/js/actions/system/api-database.js index 07fac7ad41..a17f455016 100644 --- a/js/actions/system/api-database.js +++ b/js/actions/system/api-database.js @@ -74,7 +74,7 @@ actions.defineHttp({ for (var i = 0; i < collections.length; ++i) { collection = collections[i]; - result.push({ id : collection._id, name : collection._name }); + result.push({ id : collection._id, name : collection.name() }); } actions.result(req, res, actions.HTTP_OK, result); @@ -129,7 +129,7 @@ function GET_api_database_collection (req, res) { var result = {}; result.id = collection._id; - result.name = collection._name; + result.name = collection.name(); actions.resultOk(req, res, actions.HTTP_OK, result); } @@ -199,7 +199,7 @@ function POST_api_database_collection (req, res) { actions.VERR_COLLECTION_EXISTS, "collection already exists", undefined, - { name : collection._name, id : collection._id }); + { name : collection.name(), id : collection._id }); } else { collection = db[name]; @@ -219,7 +219,7 @@ function POST_api_database_collection (req, res) { var result = {}; result.id = collection._id; - result.name = collection._name; + result.name = collection.name(); collection.parameter({ waitForSync : waitForSync }); diff --git a/js/actions/system/api-help.js b/js/actions/system/api-help.js deleted file mode 100644 index 6135986ed7..0000000000 --- a/js/actions/system/api-help.js +++ /dev/null @@ -1,101 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// @brief JavaScript actions modules -/// -/// @file -/// -/// DISCLAIMER -/// -/// Copyright 2012 triagens GmbH, Cologne, Germany -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// -/// Copyright holder is triAGENS GmbH, Cologne, Germany -/// -/// @author Achim Brandt -/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany -//////////////////////////////////////////////////////////////////////////////// - -var actions = require("actions"); - -// ----------------------------------------------------------------------------- -// --SECTION-- private variables -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup AvocadoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -var API = "_api/"; -var ApiRequests = {}; - -ApiRequests.cursor = {}; -ApiRequests.cursor["POST /" + API + "cursor"] = "create and execute query. (creates a cursor)"; -ApiRequests.cursor["PUT /" + API + "cursor/"] = "get next results"; -ApiRequests.cursor["DELETE /" + API + "cursor/"] = "delete cursor"; - -ApiRequests.collection = {}; -ApiRequests.collection["GET /" + API + "collections"] = "get list of collections"; -ApiRequests.collection["GET /" + API + "collection/"] = "get all elements of collection"; - -ApiRequests.document = {}; -ApiRequests.document["POST /" + API + "document/"] = "create new document"; -ApiRequests.document["PUT /" + API + "document//"] = "update document"; -ApiRequests.document["GET /" + API + "document//"] = "get a document"; -ApiRequests.document["DELETE /" + API + "document//"] = "delete a document"; - -ApiRequests.query = {}; -ApiRequests.query["POST /" + API + "query"] = "create a query"; -ApiRequests.query["GET /" + API + "query/"] = "get query"; -ApiRequests.query["PUT /" + API + "query/"] = "change query"; -ApiRequests.query["DELETE /" + API + "query/"] = "delete query"; - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- public functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup AvocadoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief returns a help -//////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url : API + "help", - context : "api", - - callback : function (req, res) { - var result = { - requests : ApiRequests - } - - actions.actionResultOK(req, res, 200, result); - } -}); - - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// Local Variables: -// mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" -// End: diff --git a/js/actions/system/aql-query.js b/js/actions/system/api-query.js similarity index 86% rename from js/actions/system/aql-query.js rename to js/actions/system/api-query.js index 8d0a731004..50d034e275 100644 --- a/js/actions/system/aql-query.js +++ b/js/actions/system/api-query.js @@ -42,7 +42,7 @@ var actions = require("actions"); function postQuery(req, res) { if (req.suffix.length != 0) { - actions.actionResultError (req, res, 404, actions.errorInvalidRequest, "Invalid request"); + actions.resultError (req, res, 404, actions.errorInvalidRequest, "Invalid request"); return; } @@ -50,22 +50,22 @@ function postQuery(req, res) { var json = JSON.parse(req.requestBody); if (!json || !(json instanceof Object) || json.query == undefined) { - actions.actionResultError (req, res, 400, actions.errorQuerySpecificationInvalid, "Query specification invalid"); + actions.resultError (req, res, 400, actions.errorQuerySpecificationInvalid, "Query specification invalid"); return; } var result = AQL_PARSE(json.query); if (result instanceof AvocadoQueryError) { - actions.actionResultError (req, res, 404, result.code, result.message); + actions.resultError (req, res, 404, result.code, result.message); return; } result = { "bindVars" : result }; - actions.actionResultOK(req, res, 200, result); + actions.resultOk (req, res, 200, result); } catch (e) { - actions.actionResultError (req, res, 404, actions.errorJavascriptException, "Javascript exception"); + actions.resultError (req, res, 404, actions.errorJavascriptException, "Javascript exception"); } } @@ -88,7 +88,7 @@ actions.defineHttp({ break; default: - actions.actionResultUnsupported(req, res); + actions.resultUnsupported(req, res); } } }); diff --git a/js/actions/system/api_collection.js b/js/actions/system/api_collection.js deleted file mode 100644 index f471cae198..0000000000 --- a/js/actions/system/api_collection.js +++ /dev/null @@ -1,388 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// @brief querying and managing collections -/// -/// @file -/// -/// DISCLAIMER -/// -/// Copyright 2012 triagens GmbH, Cologne, Germany -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// -/// Copyright holder is triAGENS GmbH, Cologne, Germany -/// -/// @author Achim Brandt -/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany -//////////////////////////////////////////////////////////////////////////////// - -var actions = require("actions"); -var API = "_api/"; - -// ----------------------------------------------------------------------------- -// --SECTION-- public functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup AvocadoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief creates a collection -/// -/// @REST{POST /_api/collection} -/// -/// Creates an new collection with a given name. The request must contain an -/// object with the following attributes. -/// -/// @LIT{name}: The name of the collection. -/// -/// @LIT{waitForSync} (optional, default: false): If @LIT{true} then the data -/// is synchronised to disk before returning from a create or update of an -/// document. -/// -/// @EXAMPLES -/// -/// @verbinclude api-collection-create-collection -//////////////////////////////////////////////////////////////////////////////// - -function POST_api_collection (req, res) { - var body; - - try { - body = JSON.parse(req.requestBody || "{}") || {}; - } - catch (err) { - actions.resultBad(req, res, actions.ERROR_HTTP_CORRUPTED_JSON, err); - return; - } - - var waitForSync = false; - - if (! body.hasOwnProperty("name")) { - actions.resultBad(req, res, actions.ERROR_AVOCADO_ILLEGAL_NAME, - "name must be non-empty"); - return; - } - - var name = body.name; - - if (body.hasOwnProperty("waitForSync")) { - waitForSync = body.waitForSync; - } - - try { - var collection = db._create(name, waitForSync); - - var result = {}; - var headers = {}; - - collection.parameter({ waitForSync : waitForSync }); - - result.id = collection._id; - result.name = collection.name(); - result.waitForSync = collection.parameter().waitForSync; - result.status = collection.status(); - - headers.location = "/" + API + "collection/" + collection._id; - - actions.resultOk(req, res, actions.HTTP_OK, result, headers); - } - catch (err) { - actions.resultException(req, res, err); - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief returns a collection -/// -/// @REST{GET /_api/collection/@FA{collection-identifier}} -/// -/// The result is an objects describing the collection with the following -/// attributes: -/// -/// @LIT{id}: The identifier of the collection. -/// -/// @LIT{name}: The name of the collection. -/// -/// @LIT{waitForSync}: If @LIT{true} then creating or changing a document will -/// wait until the data has been synchronised to disk. -/// -/// @LIT{status}: The status of the collection as number. -/// -/// - 1: new born collection -/// - 2: unloaded -/// - 3: loaded -/// - 4: in the process of being unloaded -/// - 5: deleted -/// -/// Every other status indicates a corrupted collection. -/// -/// If the @FA{collection-identifier} is missing, then a @LIT{HTTP 400} is -/// returned. If the @FA{collection-identifier} is unknown, then a @LIT{HTTP -/// 404} is returned. -/// -/// It is possible to specify a name instead of an identifier. In this case the -/// response will contain a field "Location" which contains the correct -/// location. -/// -/// @REST{GET /_api/collection/@FA{collection-identifier}/count} -/// -/// In addition to the above, the result also contains the number of documents. -/// Note that this will always load the collection into memory. -/// -/// @LIT{count}: The number of documents inside the collection. -/// -/// @REST{GET /_api/collection/@FA{collection-identifier}/figures} -/// -/// In addition to the above, the result also contains the number of documents -/// and additional statistical information about the collection. Note that this -/// will always load the collection into memory. -/// -/// @LIT{count}: The number of documents inside the collection. -/// -/// @LIT{figures.alive.count}: The number of living documents. -/// -/// @LIT{figures.alive.size}: The total size in bytes used by all living -/// documents. -/// -/// @LIT{figures.dead.count}: The number of dead documents. -/// -/// @LIT{figures.dead.size}: The total size in bytes used by all dead -/// documents. -/// -/// @LIT{figures.datafile.count}: The number of active datafiles. -/// -/// @LIT{journalSize}: The maximal size of the journal in bytes. -/// -/// @EXAMPLES -/// -/// Using an identifier: -/// -/// @verbinclude api-collection-get-collection-identifier -/// -/// Using a name: -/// -/// @verbinclude api-collection-get-collection-name -/// -/// Using an identifier and requesting the number of documents: -/// -/// @verbinclude api-collection-get-collection-count -/// -/// Using an identifier and requesting the figures of the collection: -/// -/// @verbinclude api-collection-get-collection-figures -//////////////////////////////////////////////////////////////////////////////// - -function GET_api_collection (req, res) { - if (req.suffix.length == 0) { - actions.resultBad(req, res, actions.ERROR_HTTP_BAD_PARAMETER, - "expected GET /" + API + "collection/") - } - else { - var name = decodeURIComponent(req.suffix[0]); - var id = parseInt(name) || name; - var collection = db._collection(id); - - if (collection == null) { - actions.collectionNotFound(req, res, name); - } - else { - - // ............................................................................. - // /_api/collection/ - // ............................................................................. - - if (req.suffix.length == 1) { - var result = {}; - var headers = {}; - var parameter = collection.parameter(); - - result.id = collection._id; - result.name = collection.name(); - result.waitForSync = parameter.waitForSync; - result.status = collection.status(); - - headers.location = "/" + API + "collection/" + collection._id; - - actions.resultOk(req, res, actions.HTTP_OK, result, headers); - } - - else if (req.suffix.length == 2) { - var sub = decodeURIComponent(req.suffix[1]); - - // ............................................................................. - // /_api/collection//figures - // ............................................................................. - - if (sub == "figures") { - var result = {}; - var headers = {}; - var parameter = collection.parameter(); - - result.id = collection._id; - result.name = collection.name(); - result.count = collection.count(); - result.journalSize = parameter.journalSize; - result.waitForSync = parameter.waitForSync; - - var figures = collection.figures(); - - if (figures) { - result.figures = { - alive : { - count : figures.numberAlive, - size : figures.sizeAlive - }, - dead : { - count : figures.numberDead, - size : figures.sizeDead - }, - datafiles : { - count : figures.numberDatafiles - } - }; - } - - result.status = collection.status(); - - headers.location = "/" + API + "collection/" + collection._id + "/figures"; - - actions.resultOk(req, res, actions.HTTP_OK, result, headers); - } - - // ............................................................................. - // /_api/collection//count - // ............................................................................. - - else if (sub == "count") { - var result = {}; - var headers = {}; - var parameter = collection.parameter(); - - result.id = collection._id; - result.name = collection.name(); - result.count = collection.count(); - result.waitForSync = parameter.waitForSync; - result.status = collection.status(); - - headers.location = "/" + API + "collection/" + collection._id + "/count"; - - actions.resultOk(req, res, actions.HTTP_OK, result, headers); - } - - else { - actions.resultNotFound(req, res, "expecting one of the sub-method 'count', 'figures'"); - } - } - else { - actions.resultBad(req, res, actions.ERROR_HTTP_BAD_PARAMETER, - "expect GET /" + API + "collection//") - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief deletes a collection -/// -/// @REST{DELETE /_api/collection/@FA{collection-identifier}} -/// -/// Deletes a collection identified by @FA{collection-identified}. -/// -/// If the collection was successfully deleted then, an object is returned with -/// the following attributes: -/// -/// @LIT{error}: @LIT{false} -/// -/// @LIT{id}: The identifier of the deleted collection. -/// -/// If the @FA{collection-identifier} is missing, then a @LIT{HTTP 400} is -/// returned. If the @FA{collection-identifier} is unknown, then a @LIT{HTTP -/// 404} is returned. -/// -/// It is possible to specify a name instead of an identifier. -/// -/// @EXAMPLES -/// -/// Using an identifier: -/// -/// @verbinclude api-collection-delete-collection-identifier -/// -/// Using a name: -/// -/// @verbinclude api-collection-delete-collection-name -//////////////////////////////////////////////////////////////////////////////// - -function DELETE_api_collection (req, res) { - if (req.suffix.length != 1) { - actions.resultBad(req, res, actions.ERROR_HTTP_BAD_PARAMETER, - "expected DELETE /" + API + "collection/") - } - else { - var name = decodeURIComponent(req.suffix[0]); - var id = parseInt(name) || name; - var collection = db._collection(id); - - if (collection == null) { - actions.collectionNotFound(req, res, name); - } - else { - try { - var result = { - id : collection._id - }; - - collection.drop(); - - actions.resultOk(req, res, actions.HTTP_OK, result); - } - catch (err) { - actions.resultException(req, res, err); - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief reads or creates a collection -//////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url : API + "collection", - context : "api", - - callback : function (req, res) { - if (req.requestType == actions.GET) { - GET_api_collection(req, res); - } - else if (req.requestType == actions.DELETE) { - DELETE_api_collection(req, res); - } - else if (req.requestType == actions.POST) { - POST_api_collection(req, res); - } - else { - actions.resultUnsupported(req, res); - } - } -}); - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// Local Variables: -// mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" -// End: diff --git a/js/actions/system/api_database.js b/js/actions/system/api_database.js deleted file mode 100644 index 07fac7ad41..0000000000 --- a/js/actions/system/api_database.js +++ /dev/null @@ -1,261 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// @brief querying and managing collections -/// -/// @file -/// -/// DISCLAIMER -/// -/// Copyright 2012 triagens GmbH, Cologne, Germany -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// -/// Copyright holder is triAGENS GmbH, Cologne, Germany -/// -/// @author Achim Brandt -/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany -//////////////////////////////////////////////////////////////////////////////// - -var actions = require("actions"); -var API = "_api/database/"; - -// ----------------------------------------------------------------------------- -// --SECTION-- public functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup AvocadoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @fn JSA_GET_api_datebase_collections -/// @brief returns all collections -/// -/// @REST{GET /_api/database/collections} -/// -/// Returns all collections. The result is a list of objects with the following -/// attributes: -/// -/// @FA{id} -/// -/// The identifier of the collection. -/// -/// @FA{name} -/// -/// The name of the collection. -/// -/// @EXAMPLES -/// -/// @verbinclude api_database1 -//////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url : API + "collections", - context : "api", - - callback : function (req, res) { - if (req.requestType != actions.GET) { - actions.resultUnsupported(req, res); - } - else { - var collections = db._collections(); - var result = []; - - for (var i = 0; i < collections.length; ++i) { - collection = collections[i]; - - result.push({ id : collection._id, name : collection._name }); - } - - actions.result(req, res, actions.HTTP_OK, result); - } - } -}); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief returns information about a collection -/// -/// @REST{GET /_api/database/collection/@FA{collection-identifier}} -/// -/// The result is an objects with the following attributes: -/// -/// @FA{id} -/// -/// The identifier of the collection. -/// -/// @FA{name} -/// -/// The name of the collection. -/// -/// @EXAMPLES -/// -/// Using a name: -/// -/// @verbinclude api_database2 -/// -/// Using an identifier: -/// -/// @verbinclude api_database3 -//////////////////////////////////////////////////////////////////////////////// - -function GET_api_database_collection (req, res) { - if (req.suffix.length != 1) { - actions.collectionUnknown(req, res); - } - else { - var name = req.suffix[0]; - var id = parseInt(name); - - if (id != NaN) { - name = id; - } - - var collection = db._collection(name); - - if (collection == null) { - actions.collectionUnknown(req, res, name); - } - else { - var result = {}; - - result.id = collection._id; - result.name = collection._name; - - actions.resultOk(req, res, actions.HTTP_OK, result); - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief creates a new collection -/// -/// @REST{POST /_api/database/collection} -/// -/// Creates a new collection. If the collection could be create, a @LIT{HTTP 200} -/// is returned. If the collection already exists, a @LIT{HTTP 409} is -/// returned. -/// -/// The call expects a JSON hash array as body with the following -/// attributes: -/// -/// @FA{name} -/// -/// The name of the collection. -/// -/// @FA{waitForSync} (optional, default true) -/// -/// If @FA{waitForSync} is false, then creation of documents will not wait -/// for the synchronization to file. -/// -/// In case of success, returns information about the created collection: -/// -/// @FA{id} -/// -/// The identifier of the collection. -/// -/// @FA{name} -/// -/// The name of the collection. -/// -/// @EXAMPLES -/// -/// Create a collection named test: -/// -/// @verbinclude api_database4 -/// -/// Try it again: -/// -/// @verbinclude api_database5 -//////////////////////////////////////////////////////////////////////////////// - -function POST_api_database_collection (req, res) { - var body = JSON.parse(req.requestBody || "{}"); - var name = body.name; - var waitForSync = true; - - if (body.hasOwnProperty("waitForSync")) { - waitForSync = body.waitForSync; - } - - if (name == null) { - badParameter(req, res, "name"); - } - else { - var collection = db._collection(name); - - if (collection != null) { - actions.error(req, res, - actions.HTTP_CONFLICT, - actions.VERR_COLLECTION_EXISTS, - "collection already exists", - undefined, - { name : collection._name, id : collection._id }); - } - else { - collection = db[name]; - - if (collection == null) { - actions.badParameter(req, res, "cannot create collection named '" + name + "'"); - } - else { - if (collection._id == 0) { - collection.load(); - } - - if (collection._id == 0) { - actions.badParameter(req, res, "cannot create collection named '" + name + "'"); - } - else { - var result = {}; - - result.id = collection._id; - result.name = collection._name; - - collection.parameter({ waitForSync : waitForSync }); - - actions.resultOk(req, res, actions.HTTP_OK, result); - } - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief reads or creates a collection -//////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url : API + "collection", - context : "api", - - callback : function (req, res) { - if (req.requestType == actions.GET) { - GET_api_database_collection(req, res); - } - else if (req.requestType == actions.POST) { - POST_api_database_collection(req, res); - } - else { - actions.resultUnsupported(req, res); - } - } -}); - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// Local Variables: -// mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" -// End: diff --git a/js/actions/system/api_simple.js b/js/actions/system/api_simple.js deleted file mode 100644 index 899f1950bc..0000000000 --- a/js/actions/system/api_simple.js +++ /dev/null @@ -1,423 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// @brief simple queries -/// -/// @file -/// -/// DISCLAIMER -/// -/// Copyright 2012 triagens GmbH, Cologne, Germany -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// -/// Copyright holder is triAGENS GmbH, Cologne, Germany -/// -/// @author Achim Brandt -/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany -//////////////////////////////////////////////////////////////////////////////// - -var actions = require("actions"); -var simple = require("simple-query"); -var API = "_api/simple/"; - -// ----------------------------------------------------------------------------- -// --SECTION-- public functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup AvocadoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @fn JSA_PUT_api_simple_all -/// @brief returns all documents of a collection -/// -/// @REST{PUT /_api/simple/all} -/// -/// Returns all documents of a collections. The call expects a JSON hash array -/// as body with the following attributes: -/// -/// @FA{collection} -/// -/// The identifier or name of the collection to query. -/// -/// @FA{skip} (optional) -/// -/// The documents to skip in the query. -/// -/// @FA{limit} (optional) -/// -/// The maximal amount of documents to return. -/// -/// @EXAMPLES -/// -/// To get all documents (NEVER DO THAT!) -/// -/// @verbinclude api_simple1 -/// -/// Limit the amount of documents using -/// -/// @verbinclude api_simple2 -//////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url : API + "all", - context : "api", - - callback : function (req, res) { - var body = JSON.parse(req.requestBody || "{}"); - - var limit = body.limit; - var skip = body.skip; - var name = body.collection; - - if (req.requestType != actions.PUT) { - actions.unsupported(req, res); - } - else { - collection = db._collection(name); - - if (collection == null) { - actions.collectionUnknown(req, res, name); - } - else { - var result = collection.all(); - - if (skip != null) { - result = result.skip(skip); - } - - if (limit != null) { - result = result.limit(limit); - } - - actions.result(req, res, actions.HTTP_OK, result.toArray()); - } - } - } -}); - -//////////////////////////////////////////////////////////////////////////////// -/// @fn JSA_PUT_api_simple_near -/// @brief returns all documents of a collection near a given location -/// -/// @REST{PUT /_api/simple/near} -/// -/// The default will find at most 100 documents near a given coordinate. The -/// returned list is sorted according to the distance, with the nearest document -/// coming first. If there are near documents of equal distance, documents are -/// chosen randomly from this set until the limit is reached. It is possible to -/// change the limit using the @FA{limit} operator. -/// -/// In order to use the @FN{near} operator, a geo index must be defined for the -/// collection. This index also defines which attribute holds the coordinates -/// for the document. If you have more then one geo-spatial index, you can use -/// the @FN{geo} operator to select a particular index. -/// -/// The call expects a JSON hash array as body with the following attributes: -/// -/// @FA{collection} -/// -/// The identifier or name of the collection to query. -/// -/// @FA{latitude} -/// -/// The latitude of the coordinate. -/// -/// @FA{longitude} -/// -/// The longitude of the coordinate. -/// -/// @FA{distance} (optional) -/// -/// If given, the attribute key used to store the distance. -/// -/// @FA{skip} (optional) -/// -/// The documents to skip in the query. -/// -/// @FA{limit} (optional) -/// -/// The maximal amount of documents to return. -/// -/// @FA{geo} (optional) -/// -/// If given, the identifier of the geo-index to use. -/// -/// @EXAMPLES -/// -/// Without distance: -/// -/// @verbinclude api_simple3 -/// -/// With distance: -/// -/// @verbinclude api_simple4 -//////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url : API + "near", - context : "api", - - callback : function (req, res) { - var body = JSON.parse(req.requestBody || "{}"); - - var limit = body.limit; - var skip = body.skip; - var latitude = body.latitude; - var longitude = body.longitude; - var distance = body.distance; - var name = body.collection; - var geo = body.geo; - - if (req.requestType != actions.PUT) { - actions.unsupported(req, res); - } - else { - collection = db._collection(name); - - if (collection == null) { - actions.collectionUnknown(req, res, name); - } - else if (latitude == null) { - actions.badParameter(req, res, "latitude"); - } - else if (longitude == null) { - actions.badParameter(req, res, "longitude"); - } - else { - var result; - - if (geo == null) { - result = collection.near(latitude, longitude); - } - else { - result = collection.geo(geo).near(latitude, longitude); - } - - if (skip != null) { - result = result.skip(skip); - } - - if (limit != null) { - result = result.limit(limit); - } - - if (distance != null) { - result = result.distance(distance); - } - - actions.result(req, res, actions.HTTP_OK, result.toArray()); - } - } - } -}); - -//////////////////////////////////////////////////////////////////////////////// -/// @fn JSA_PUT_api_simple_within -/// @brief returns all documents of a collection within a given radius -/// -/// @REST{PUT /_api/simple/within} -/// -/// This will find all documents with in a given radius around the coordinate -/// (@FA{latitude}, @FA{longitude}). The returned list is sorted by distance. -/// -/// In order to use the @FN{within} operator, a geo index must be defined for the -/// collection. This index also defines which attribute holds the coordinates -/// for the document. If you have more then one geo-spatial index, you can use -/// the @FN{geo} operator to select a particular index. -/// -/// The call expects a JSON hash array as body with the following attributes: -/// -/// @FA{collection} -/// -/// The identifier or name of the collection to query. -/// -/// @FA{latitude} -/// -/// The latitude of the coordinate. -/// -/// @FA{longitude} -/// -/// The longitude of the coordinate. -/// -/// @FA{radius} -/// -/// The maximal radius. -/// -/// @FA{distance} (optional) -/// -/// If given, the attribute key used to store the distance. -/// -/// @FA{skip} (optional) -/// -/// The documents to skip in the query. -/// -/// @FA{limit} (optional) -/// -/// The maximal amount of documents to return. -/// -/// @FA{geo} (optional) -/// -/// If given, the identifier of the geo-index to use. -/// -/// @EXAMPLES -/// -/// Without distance: -/// -/// @verbinclude api_simple5 -/// -/// With distance: -/// -/// @verbinclude api_simple6 -//////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url : API + "within", - context : "api", - - callback : function (req, res) { - var body = JSON.parse(req.requestBody || "{}"); - - var limit = body.limit; - var skip = body.skip; - var latitude = body.latitude; - var longitude = body.longitude; - var distance = body.distance; - var radius = body.radius; - var name = body.collection; - var geo = body.geo; - - if (req.requestType != actions.PUT) { - actions.unsupported(req, res); - } - else { - collection = db._collection(name); - - if (collection == null) { - actions.collectionUnknown(req, res, name); - } - else if (latitude == null) { - actions.badParameter(req, res, "latitude"); - } - else if (longitude == null) { - actions.badParameter(req, res, "longitude"); - } - else { - var result; - - if (geo == null) { - result = collection.within(latitude, longitude, radius); - } - else { - result = collection.geo(geo).within(latitude, longitude, radius); - } - - if (skip != null) { - result = result.skip(skip); - } - - if (limit != null) { - result = result.limit(limit); - } - - if (distance != null) { - result = result.distance(distance); - } - - actions.result(req, res, actions.HTTP_OK, result.toArray()); - } - } - } -}); - -//////////////////////////////////////////////////////////////////////////////// -/// @fn JSA_PUT_api_simple_by_example -/// @brief returns all documents of a collection matching a given example -/// -/// @REST{PUT /_api/simple/by-example} -/// -/// This will find all documents matching a given example. -/// -/// The call expects a JSON hash array as body with the following attributes: -/// -/// @FA{collection} -/// -/// The identifier or name of the collection to query. -/// -/// @FA{example} -/// -/// The example. -/// -/// @FA{skip} (optional) -/// -/// The documents to skip in the query. -/// -/// @FA{limit} (optional) -/// -/// The maximal amount of documents to return. -/// -/// @EXAMPLES -/// -/// @verbinclude api_simple7 -//////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url : API + "by-example", - context : "api", - - callback : function (req, res) { - var body = JSON.parse(req.requestBody || "{}"); - - var limit = body.limit; - var skip = body.skip; - var name = body.collection; - var example = body.example; - - if (req.requestType != actions.PUT) { - actions.unsupported(req, res); - } - else { - collection = db._collection(name); - - if (collection == null) { - actions.collectionUnknown(req, res, name); - } - else if (typeof example !== "object") { - actions.badParameter(req, res, "example"); - } - else { - var result = collection.byExample(example); - - if (skip != null) { - result = result.skip(skip); - } - - if (limit != null) { - result = result.limit(limit); - } - - actions.result(req, res, actions.HTTP_OK, result.toArray()); - } - } - } -}); - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// Local Variables: -// mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" -// End: diff --git a/js/actions/system/collections.js b/js/actions/system/collections.js index 49658b885f..6e27ce4c4b 100644 --- a/js/actions/system/collections.js +++ b/js/actions/system/collections.js @@ -44,15 +44,16 @@ function getCollections(req, res) { for (var i = skip; i < end; ++i) { coll = colls[i]; - result.collections[coll._name] = { + var name = coll.name(); + result.collections[name] = { _id : coll._id, - name : coll._name, + name : name, status : coll.status(), figures : coll.figures() }; } - actions.actionResultOK(req, res, 200, result); + actions.resultOk(req, res, 200, result); } actions.defineHttp({ @@ -66,7 +67,7 @@ actions.defineHttp({ break; default: - actions.actionResultUnsupported(req, res); + actions.resultUnsupported(req, res); } } }); diff --git a/js/actions/system/document.js b/js/actions/system/document.js deleted file mode 100644 index c186a72b5a..0000000000 --- a/js/actions/system/document.js +++ /dev/null @@ -1,127 +0,0 @@ -var actions = require("actions"); - -function getDocument(req, res) { - if (req.suffix.length != 2) { - actions.actionResultError (req, res, 404, actions.documentNotFound, "Document not found"); - return; - } - - try { - var collection = decodeURIComponent(req.suffix[0]); - var documentId = decodeURIComponent(req.suffix[1]); - var result = { - "document" : {} - }; - - result.document = db[collection].document(documentId); - actions.actionResultOK(req, res, 200, result); - } - catch (e) { - actions.actionResultError (req, res, 404, actions.documentNotFound, "Document not found: " + e); - } -} - -function deleteDocument(req, res) { - if (req.suffix.length != 2) { - actions.actionResultError (req, res, 404, actions.documentNotFound, "Document not found"); - return; - } - - try { - var collection = decodeURIComponent(req.suffix[0]); - var documentId = decodeURIComponent(req.suffix[1]); - - var result = {}; - - - if (db[collection].delete(documentId)) { - result = { - "deleted" : true, - "_id" : documentId - }; - - actions.actionResultOK(req, res, 200, result); - } - else { - actions.actionResultError (req, res, 304, actions.documentNotModified, "Document not deleted"); - } - } - catch (e) { - actions.actionResultError(req, res, 304, actions.documentNotModified, "Document not deleted: " + e); - } -} - -function postDocument(req, res) { - if (req.suffix.length != 1) { - actions.actionResultError (req, res, 404, actions.collectionNotFound, "Collection not found"); - return; - } - - try { - var collection = decodeURIComponent(req.suffix[0]); - var json = JSON.parse(req.requestBody); - var id = db[collection].save(json); - - var result = { - "created" : true, - "_id" : id - }; - - actions.actionResultOK(req, res, 201, result); - } - catch (e) { - actions.actionResultError (req, res, 404, actions.documentNotModified, "Document not saved: " + e); - } -} - -function putDocument(req, res) { - if (req.suffix.length != 2) { - actions.actionResultError (req, res, 404, actions.documentNotFound, "Document not found"); - return; - } - - try { - var collection = decodeURIComponent(req.suffix[0]); - var documentId = decodeURIComponent(req.suffix[1]); - var json = JSON.parse(req.requestBody); - var id = db[collection].replace(documentId, json); - - var result = { - "updated" : true, - "_id" : id - }; - - actions.actionResultOK(req, res, 202, result); - } - catch (e) { - actions.actionResultError (req, res, 404, actions.documentNotModified, "Document not changed: " + e); - } -} - -actions.defineHttp({ - url : "_api/document", - context : "api", - - callback : function (req, res) { - switch (req.requestType) { - case ("GET") : - getDocument(req, res); - break; - - case ("POST") : - postDocument(req, res); - break; - - case ("PUT") : - putDocument(req, res); - break; - - case ("DELETE") : - deleteDocument(req, res); - break; - - default: - actions.actionResultUnsupported(req, res); - } - } -}); diff --git a/js/actions/system/documents.js b/js/actions/system/documents.js index 1ae62003e0..e99592e618 100644 --- a/js/actions/system/documents.js +++ b/js/actions/system/documents.js @@ -2,7 +2,7 @@ var actions = require("actions"); function getDocuments(req, res) { if (req.suffix.length != 1) { - actions.actionResultError (req, res, 404, actions.collectionNotFound, "Collection not found"); + actions.resultError (req, res, 404, actions.collectionNotFound, "Collection not found"); return; } @@ -26,10 +26,10 @@ function getDocuments(req, res) { try { var result = db[collection].ALL(skip, limit); - actions.actionResultOK(req, res, 200, result); + actions.resultOk(req, res, 200, result); } catch (e) { - actions.actionResultError (req, res, 404, actions.collectionNotFound, "Collection not found") + actions.resultError (req, res, 404, actions.collectionNotFound, "Collection not found") } } @@ -44,7 +44,7 @@ actions.defineHttp({ break; default: - actions.actionResultUnsupported(req, res); + actions.resultUnsupported(req, res); } }, diff --git a/js/actions/system/front-end.js b/js/actions/system/front-end.js index 4edc6d13d4..846664bf0f 100644 --- a/js/actions/system/front-end.js +++ b/js/actions/system/front-end.js @@ -36,57 +36,6 @@ var actions = require("actions"); /// @{ //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @brief returns information about all collections -/// -/// @REST{GET /_system/collections} -/// -/// Returns information about all collections of the database. The returned -/// array contains the following entries. -/// -/// - path: The server directory containing the database. -/// - collections : An associative array of all collections. -/// -/// An entry of collections is again an associative array containing the -/// following entries. -/// -/// - name: The name of the collection. -/// - status: The status of the collection. 1 = new born, 2 = unloaded, -/// 3 = loaded, 4 = corrupted. -/// -/// @verbinclude rest15 -//////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url : "_system/collections", - context : "admin", - - callback : function (req, res) { - var colls; - var coll; - var result; - - colls = db._collections(); - result = { - path : db._path, - collections : {} - }; - - for (var i = 0; i < colls.length; ++i) { - coll = colls[i]; - - result.collections[coll._name] = { - id : coll._id, - name : coll._name, - status : coll.status(), - figures : coll.figures() - }; - } - - actions.actionResult(req, res, 200, result); - } -}); - //////////////////////////////////////////////////////////////////////////////// /// @brief loads a collection /// @@ -98,17 +47,17 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_system/collection/load", + url : "_system/collection/load", // TODO -> api_collection.js context : "admin", callback : function (req, res) { try { req.collection.load(); - actions.actionResult(req, res, 204); + actions.resultOk(req, res, 204); } catch (err) { - actions.actionError(req, res, err); + actions.resultError(req, res, err); } }, @@ -117,61 +66,6 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @brief information about a collection -/// -/// @REST{GET /_system/collection/info?collection=@FA{identifier}} -/// -/// Returns information about a collection -/// -/// @verbinclude rest16 -//////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url : "_system/collection/info", - context : "admin", - - callback : function (req, res) { - try { - result = {}; - result.id = req.collection._id; - result.name = req.collection._name; - result.status = req.collection.status(); - result.figures = req.collection.figures(); - - actions.actionResult(req, res, 200, result); - } - catch (err) { - actions.actionError(req, res, err); - } - }, - - parameters : { - collection : "collection-identifier" - } -}); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief returns information about all documents -/// -/// @REST{GET /_system/documents} -//////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url : "_system/documents", - context : "admin", - - callback : function (req, res) { - queryReferences(req, res, req.collection.all()); - }, - - parameters : { - collection : "collection-identifier", - blocksize : "number", - page : "number" - } -}); - //////////////////////////////////////////////////////////////////////////////// /// @brief returns information about all indexes of a collection /// @@ -181,20 +75,20 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_system/collection/indexes", + url : "_system/collection/indexes", // TODO api_indexes.js context : "admin", callback : function (req, res) { try { result = {}; - result.name = req.collection._name; + result.name = req.collection.name(); result.id = req.collection._id; result.indexes = req.collection.getIndexes(); - actions.actionResult(req, res, 200, result); + actions.resultOk(req, res, 200, result); } catch (err) { - actions.actionError(req, res, err); + actions.resultError(req, res, err); } }, @@ -210,7 +104,7 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_system/status", + url : "_system/status", // TODO -> _api/system context : "admin", callback : function (req, res) { @@ -218,10 +112,10 @@ actions.defineHttp({ result = {}; result.system = SYS_PROCESS_STAT(); - actions.actionResult(req, res, 200, result); + actions.resultOk(req, res, 200, result); } catch (err) { - actions.actionError(req, res, err); + actions.resultError(req, res, err); } } }); diff --git a/js/actions/system/key-value.js b/js/actions/system/key-value.js index 8698252cdd..600c0010d3 100644 --- a/js/actions/system/key-value.js +++ b/js/actions/system/key-value.js @@ -113,7 +113,7 @@ function buildDocumentFromReq(req) { function postKeyValue(req, res) { if (req.suffix.length < 2) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not created. Missing key."); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Key value pair not created. Missing key."); return; } @@ -121,7 +121,7 @@ function postKeyValue(req, res) { var collection = req.suffix[0]; if (db._collection(collection) == null) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Collection not found."); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Collection not found."); return; } @@ -131,7 +131,7 @@ function postKeyValue(req, res) { s.execute(); if (s._countTotal != 0) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Use PUT to change value"); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Use PUT to change value"); } else { var id = db[collection].save(doc); @@ -139,11 +139,11 @@ function postKeyValue(req, res) { "saved" : true, "_id" : id } - actions.actionResultOK(req, res, 201, result); + actions.resultOk(req, res, 201, result); } } catch (e) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not created. " + e.message); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Key value pair not created. " + e.message); } } @@ -153,7 +153,7 @@ function postKeyValue(req, res) { function putKeyValue(req, res) { if (req.suffix.length < 2) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); return; } @@ -161,7 +161,7 @@ function putKeyValue(req, res) { var collection = req.suffix[0]; if (db._collection(collection) == null) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Collection not found."); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Collection not found."); return; } @@ -177,13 +177,13 @@ function putKeyValue(req, res) { "saved" : true, "_id" : id } - actions.actionResultOK(req, res, 201, result); + actions.resultOk(req, res, 201, result); return; } - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); } else if (s._countTotal > 1) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. Wrong key?"); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. Wrong key?"); } else { // get _id @@ -197,15 +197,15 @@ function putKeyValue(req, res) { // replace the document if (db[collection].replace(id, doc)) { - actions.actionResultOK(req, res, 202, {"changed" : true}); + actions.resultOk(req, res, 202, {"changed" : true}); } else { - actions.actionResultError(req, res, 404, actions.keyValueNotModified, "Value not changed"); + actions.resultError(req, res, 404, actions.keyValueNotModified, "Value not changed"); } } } catch (e) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. " + e.message); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. " + e.message); } } @@ -215,7 +215,7 @@ function putKeyValue(req, res) { function deleteKeyValue(req, res) { if (req.suffix.length < 2) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); return; } @@ -223,7 +223,7 @@ function deleteKeyValue(req, res) { var collection = req.suffix[0]; if (db._collection(collection) == null) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Collection not found."); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Collection not found."); return; } @@ -237,23 +237,23 @@ function deleteKeyValue(req, res) { s.execute(); if (s._countTotal < 1) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); } else if (s._countTotal > 1) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. Wrong key?"); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. Wrong key?"); } else { var id = s._execution._documents[0]._id; if (db[collection].delete(id)) { - actions.actionResultOK(req, res, 202, {"removed" : true}); + actions.resultOk(req, res, 202, {"removed" : true}); } else { - actions.actionResultError(req, res, 404, actions.keyValueNotModified, "Value not removed"); + actions.resultError(req, res, 404, actions.keyValueNotModified, "Value not removed"); } } } catch (e) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. " + e.message); + actions.resultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. " + e.message); } } @@ -263,7 +263,7 @@ function deleteKeyValue(req, res) { function getKeyValue(req, res) { if (req.suffix.length < 2) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found"); + actions.resultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found"); return; } @@ -271,7 +271,7 @@ function getKeyValue(req, res) { var collection = req.suffix[0]; if (db._collection(collection) == null) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Collection not found."); + actions.resultError (req, res, 404, actions.keyValueNotFound, "Collection not found."); return; } @@ -285,10 +285,10 @@ function getKeyValue(req, res) { s.execute(); if (s._countTotal < 1) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found"); + actions.resultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found"); } else if (s._countTotal > 1) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found. Wrong key?"); + actions.resultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found. Wrong key?"); } else { var headers = {}; @@ -306,11 +306,11 @@ function getKeyValue(req, res) { headers["x-voc-created"] = formatTimeStamp(s._execution._documents[0]["x-voc-created"]); } - actions.actionResultOK(req, res, 200, s._execution._documents[0].value, headers); + actions.resultOk(req, res, 200, s._execution._documents[0].value, headers); } } catch (e) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found. " + e.message); + actions.resultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found. " + e.message); } } @@ -345,7 +345,7 @@ actions.defineHttp({ break; default: - actions.actionResultUnsupported(req, res); + actions.resultUnsupported(req, res); } } }); @@ -370,7 +370,7 @@ actions.defineHttp({ function searchKeyValue(req, res) { if (req.suffix.length < 2) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pairs not found."); + actions.resultError (req, res, 404, actions.keyValueNotFound, "Key value pairs not found."); return; } @@ -378,7 +378,7 @@ function searchKeyValue(req, res) { var collection = req.suffix[0]; if (db._collection(collection) == null) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Collection not found."); + actions.resultError (req, res, 404, actions.keyValueNotFound, "Collection not found."); return; } @@ -407,10 +407,10 @@ function searchKeyValue(req, res) { } } - actions.actionResult (req, res, 200, result); + actions.result (req, res, 200, result); } catch (e) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pairs not found. " + e.message); + actions.resultError (req, res, 404, actions.keyValueNotFound, "Key value pairs not found. " + e.message); } } @@ -433,7 +433,7 @@ actions.defineHttp({ break; default: - actions.actionResultUnsupported(req, res); + actions.resultUnsupported(req, res); } } }); diff --git a/js/client/client.js b/js/client/client.js index 64d8134f27..2c73e5b2dd 100644 --- a/js/client/client.js +++ b/js/client/client.js @@ -585,16 +585,8 @@ AvocadoDatabase.prototype._create = function (name) { "name" : name }; - var str = this._connection.post("/_api/database/collection", JSON.stringify(body)); + var requestResult = this._connection.post("/_api/collection", JSON.stringify(body)); - print(str); - - var requestResult = undefined; - - if (str != undefined) { - requestResult = JSON.parse(str); - } - if (isErrorResult(requestResult)) { return undefined; } @@ -617,278 +609,7 @@ AvocadoDatabase.prototype._help = function () { AvocadoDatabase.prototype.toString = function () { return "[object AvocadoDatabase]"; } -/* -// ----------------------------------------------------------------------------- -// --SECTION-- AvocadoStoredStatement -// ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @brief constructor -//////////////////////////////////////////////////////////////////////////////// - -function AvocadoStoredStatement (database, data) { - this._database = database; - this._doCount = false; - this._batchSize = null; - this._bindVars = {}; - this._id = null; - this.document = { - "queryCollection" : DEFAULT_QUERY_COLLECTION - }; - - if (!(data instanceof Object)) { - throw "AvocadoStoredStatement needs a data attribute"; - } - - if (data["name"] != undefined) { - this.document.name = data["name"]; - } - - if (data["query"] != undefined) { - this.document.query = data["query"]; - } - - if (data["queryCollection"] != undefined) { - this.document.queryCollection = data["queryCollection"]; - } - - this._isNew = (data["query"] != undefined); - - this.validate(); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief update a stored statement -//////////////////////////////////////////////////////////////////////////////// - -AvocadoStoredStatement.prototype.update = function (data) { - // update query string - if (data["query"] != undefined) { - this.document.query = data["query"]; - } - - this.validate(); - - var queryCollection = new AvocadoCollection(this._database, this.document.queryCollection); - if (!queryCollection) { - throw "Could not determine collection for AvocadoStoredStatement"; - } - - if (this._isNew) { - var requestResult = queryCollection.save(this.document); - if (requestResult == undefined) { - throw "Could not save AvocadoStoredStatement"; - } - - // document saved - this._id = requestResult; - this._isNew = false; - return true; - } - - if (!queryCollection.update(this.document._id, this.document)) { - throw "Could not update AvocadoStoredStatement"; - } - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief save a stored statement -//////////////////////////////////////////////////////////////////////////////// - -AvocadoStoredStatement.prototype.save = function () { - return this.update(this.document); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief validate the data of an AvocadoStoredStatement -//////////////////////////////////////////////////////////////////////////////// - -AvocadoStoredStatement.prototype.validate = function () { - if (this._isNew) { - if (this.document.query == undefined || this.document.query == "") { - throw "AvocadoStoredStatement needs a valid query"; - } - } - - if (this.document.name == undefined || this.document.name == "") { - throw "AvocadoStoredStatement needs a name attribute"; - } - - if (this.document.queryCollection == undefined || this.document.queryCollection == "") { - throw "AvocadoStoredStatement needs a queryCollection"; - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief lookup the data of an AvocadoStoredStatement -//////////////////////////////////////////////////////////////////////////////// - -AvocadoStoredStatement.prototype.lookup = function () { - if (this.isNew) { - throw "Cannot lookup a new AvocadoStoredStatement"; - } - - var data = { - "query" : "SELECT c FROM `" + this.document.queryCollection + - "` c WHERE c.name == '" + QuoteJSONString(this.document.name) + "'" - } - var statement = new AvocadoStatement(this._database, data); - var result = statement.execute(); - if (result instanceof AvocadoQueryError) { - throw result.message; - } - - if (!result.hasNext()) { - throw "Could not find stored statement for the given parameters"; - } - - var row = result.next(); - this._id = row["id"]; - this._query = row["query"]; - this._isNew = false; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief delete a stored statement -//////////////////////////////////////////////////////////////////////////////// - -AvocadoStoredStatement.prototype.delete = function () { - if (this._isNew) { - throw "Cannot delete a new AvocadoStoredStatement"; - } - - if (this._id == undefined || this._id == null) { - this.lookup(); - } - - var queryCollection = new AvocadoCollection(this._database, this.document.collection); - if (!queryCollection) { - throw "Could not determine collection for AvocadoStoredStatement"; - } - - if (!queryCollection.delete(this.document._id)) { - this.document = {}; - this._isNew = true; - this._bindVars = {}; - this._id = null; - return true; - } - - return false; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief bind a parameter to the statement -/// -/// This function can be called multiple times, once for each bind parameter. -/// All bind parameters will be transferred to the server in one go when -/// execute() is called. -//////////////////////////////////////////////////////////////////////////////// - -AvocadoStoredStatement.prototype.bind = function (key, value) { - if (typeof(key) != "string") { - throw "bind parameter name must be a string"; - } - - if (this._bindVars[key] != undefined) { - throw "redeclaration of bind parameter"; - } - - this._bindVars[key] = value; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief set the count flag for the statement -/// -/// Setting the count flag will make the query instance's cursor return the -/// total number of result documents. The count flag is not set by default. -//////////////////////////////////////////////////////////////////////////////// - -AvocadoStoredStatement.prototype.setCount = function (bool) { - this._doCount = bool ? true : false; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief set the maximum number of results documents the cursor will return -/// in a single server roundtrip. -/// The higher this number is, the less server roundtrips will be made when -/// iterating over the result documents of a cursor. -//////////////////////////////////////////////////////////////////////////////// - -AvocadoStoredStatement.prototype.setBatchSize = function (value) { - if (parseInt(value) > 0) { - this._batchSize = parseInt(value); - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief execute the query -/// -/// Invoking execute() will transfer the query and all bind parameters to the -/// server. It will return a cursor with the query results in case of success. -/// In case of an error, the error will be printed -//////////////////////////////////////////////////////////////////////////////// - -AvocadoStoredStatement.prototype.execute = function () { - if (this._isNew) { - this.save(); - } - - var body = { - "name" : this.document.name, - "count" : this._doCount, - "bindVars" : this._bindVars, - "_id" : this._id - } - - if (this._batchSize) { - body["batchSize"] = this._batchSize; - } - - var requestResult = this._database._connection.post("/_api/cursor", JSON.stringify(body)); - - if (isErrorResult(requestResult)) { - return undefined; - } - - return new AvocadoQueryCursor(this._database, requestResult); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief print the help for AvocadoStoredStatement -//////////////////////////////////////////////////////////////////////////////// - -AvocadoStoredStatement.prototype._help = function () { - print(helpAvocadoStoredStatement); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief return a string representation of the stored statement -//////////////////////////////////////////////////////////////////////////////// - -AvocadoStoredStatement.prototype.toString = function () { - return getIdString(this, "AvocadoStoredStatement"); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief factory method to create a new stored statement -//////////////////////////////////////////////////////////////////////////////// - -AvocadoDatabase.prototype._createStoredStatement = function (data) { - return new AvocadoStoredStatement(this, data); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief factory method to retrieve an existing stored statement -//////////////////////////////////////////////////////////////////////////////// - -AvocadoDatabase.prototype._getStoredStatement = function (data) { - return new AvocadoStoredStatement(this, data); -} - -*/ // ----------------------------------------------------------------------------- // --SECTION-- AvocadoStatement // ----------------------------------------------------------------------------- @@ -977,7 +698,7 @@ AvocadoStatement.prototype.getCount = function () { /// in a single server roundtrip. //////////////////////////////////////////////////////////////////////////////// -AvocadoStatement.prototype.getMax = function () { +AvocadoStatement.prototype.getBatchSize = function () { return this._batchSize; } @@ -1126,7 +847,7 @@ getHeadline("Select query help") + ' > st.setCount(); set count flag (return number of ' + "\n" + ' results in "count" attribute) ' + "\n" + 'Get query options: ' + "\n" + -' > st.getMax(); return the max. number of results ' + "\n" + +' > st.setBatchSize(); return the max. number of results ' + "\n" + ' to be transferred per roundtrip ' + "\n" + ' > st.getCount(); return count flag (return number of' + "\n" + ' results in "count" attribute) ' + "\n" + @@ -1205,7 +926,7 @@ getHeadline("AvocadoStatement help") + ' to be transferred per roundtrip ' + "\n" + ' setCount(); set count flag (return number of ' + "\n" + ' results in "count" attribute) ' + "\n" + -' getMax(); return max. number of results ' + "\n" + +' getBatchSize(); return max. number of results ' + "\n" + ' to be transferred per roundtrip ' + "\n" + ' getCount(); return count flag (return number of' + "\n" + ' results in "count" attribute) ' + "\n" + @@ -1221,32 +942,7 @@ getHeadline("AvocadoStatement help") + ' > st.bind("b", "world"); ' + "\n" + ' > c = st.execute(); ' + "\n" + ' > print(c.elements()); '; -/* -helpAvocadoStoredStatement = -getHeadline("AvocadoQueryTemplate help") + -'AvocadoQueryTemplate constructor: ' + "\n" + -' > qt1 = db._createQueryTemplate("select ..."); simple query ' + "\n" + -' > qt2 = db._createQueryTemplate( complex query ' + "\n" + -' {query:"select...", ' + "\n" + -' name:"qname", ' + "\n" + -' collection:"q" ' + "\n" + -' ... } ' + "\n" + -'Functions: ' + "\n" + -' update(); update query template ' + "\n" + -' delete(); delete query template by id ' + "\n" + -' getInstance(); get a query instance ' + "\n" + -' returns: AvocadoQueryInstance' + "\n" + -' _help(); this help ' + "\n" + -'Attributes: ' + "\n" + -' _database database object ' + "\n" + -' _id template id ' + "\n" + -' name collection name ' + "\n" + -'Example: ' + "\n" + -' > qt1 = db._getQueryTemplate("4334:2334"); ' + "\n" + -' > qt1.update("select a from collA a"); ' + "\n" + -' > qi1 = qt1.getInstance(); ' + "\n" + -' > qt1.delete("4334:2334"); '; -*/ + helpExtended = getHeadline("More help") + 'Pager: ' + "\n" + diff --git a/js/client/js-client.h b/js/client/js-client.h index 2e0d95d808..0adec5731a 100644 --- a/js/client/js-client.h +++ b/js/client/js-client.h @@ -586,16 +586,8 @@ static string JS_client_client = " \"name\" : name\n" " };\n" "\n" - " var str = this._connection.post(\"/_api/database/collection\", JSON.stringify(body));\n" + " var requestResult = this._connection.post(\"/_api/collection\", JSON.stringify(body));\n" "\n" - " print(str);\n" - " \n" - " var requestResult = undefined;\n" - "\n" - " if (str != undefined) {\n" - " requestResult = JSON.parse(str);\n" - " }\n" - " \n" " if (isErrorResult(requestResult)) {\n" " return undefined;\n" " }\n" @@ -618,278 +610,7 @@ static string JS_client_client = "AvocadoDatabase.prototype.toString = function () { \n" " return \"[object AvocadoDatabase]\";\n" "}\n" - "/*\n" - "// -----------------------------------------------------------------------------\n" - "// --SECTION-- AvocadoStoredStatement\n" - "// -----------------------------------------------------------------------------\n" "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief constructor\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "function AvocadoStoredStatement (database, data) {\n" - " this._database = database;\n" - " this._doCount = false;\n" - " this._batchSize = null;\n" - " this._bindVars = {};\n" - " this._id = null;\n" - " this.document = {\n" - " \"queryCollection\" : DEFAULT_QUERY_COLLECTION\n" - " };\n" - "\n" - " if (!(data instanceof Object)) {\n" - " throw \"AvocadoStoredStatement needs a data attribute\";\n" - " }\n" - " \n" - " if (data[\"name\"] != undefined) {\n" - " this.document.name = data[\"name\"];\n" - " }\n" - " \n" - " if (data[\"query\"] != undefined) {\n" - " this.document.query = data[\"query\"];\n" - " }\n" - "\n" - " if (data[\"queryCollection\"] != undefined) {\n" - " this.document.queryCollection = data[\"queryCollection\"];\n" - " } \n" - " \n" - " this._isNew = (data[\"query\"] != undefined); \n" - "\n" - " this.validate();\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief update a stored statement\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoStoredStatement.prototype.update = function (data) {\n" - " // update query string\n" - " if (data[\"query\"] != undefined) {\n" - " this.document.query = data[\"query\"];\n" - " }\n" - "\n" - " this.validate();\n" - "\n" - " var queryCollection = new AvocadoCollection(this._database, this.document.queryCollection);\n" - " if (!queryCollection) {\n" - " throw \"Could not determine collection for AvocadoStoredStatement\";\n" - " }\n" - "\n" - " if (this._isNew) {\n" - " var requestResult = queryCollection.save(this.document);\n" - " if (requestResult == undefined) {\n" - " throw \"Could not save AvocadoStoredStatement\";\n" - " }\n" - "\n" - " // document saved\n" - " this._id = requestResult;\n" - " this._isNew = false;\n" - " return true;\n" - " }\n" - "\n" - " if (!queryCollection.update(this.document._id, this.document)) {\n" - " throw \"Could not update AvocadoStoredStatement\";\n" - " }\n" - " \n" - " return true;\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief save a stored statement\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoStoredStatement.prototype.save = function () {\n" - " return this.update(this.document);\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief validate the data of an AvocadoStoredStatement\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoStoredStatement.prototype.validate = function () {\n" - " if (this._isNew) {\n" - " if (this.document.query == undefined || this.document.query == \"\") {\n" - " throw \"AvocadoStoredStatement needs a valid query\";\n" - " }\n" - " }\n" - "\n" - " if (this.document.name == undefined || this.document.name == \"\") {\n" - " throw \"AvocadoStoredStatement needs a name attribute\";\n" - " }\n" - " \n" - " if (this.document.queryCollection == undefined || this.document.queryCollection == \"\") {\n" - " throw \"AvocadoStoredStatement needs a queryCollection\";\n" - " }\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief lookup the data of an AvocadoStoredStatement\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoStoredStatement.prototype.lookup = function () {\n" - " if (this.isNew) {\n" - " throw \"Cannot lookup a new AvocadoStoredStatement\";\n" - " }\n" - "\n" - " var data = {\n" - " \"query\" : \"SELECT c FROM `\" + this.document.queryCollection + \n" - " \"` c WHERE c.name == '\" + QuoteJSONString(this.document.name) + \"'\"\n" - " } \n" - " var statement = new AvocadoStatement(this._database, data);\n" - " var result = statement.execute();\n" - " if (result instanceof AvocadoQueryError) {\n" - " throw result.message;\n" - " }\n" - "\n" - " if (!result.hasNext()) {\n" - " throw \"Could not find stored statement for the given parameters\";\n" - " }\n" - "\n" - " var row = result.next();\n" - " this._id = row[\"id\"];\n" - " this._query = row[\"query\"];\n" - " this._isNew = false;\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief delete a stored statement\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoStoredStatement.prototype.delete = function () {\n" - " if (this._isNew) {\n" - " throw \"Cannot delete a new AvocadoStoredStatement\";\n" - " }\n" - " \n" - " if (this._id == undefined || this._id == null) {\n" - " this.lookup();\n" - " }\n" - "\n" - " var queryCollection = new AvocadoCollection(this._database, this.document.collection);\n" - " if (!queryCollection) {\n" - " throw \"Could not determine collection for AvocadoStoredStatement\";\n" - " }\n" - "\n" - " if (!queryCollection.delete(this.document._id)) {\n" - " this.document = {};\n" - " this._isNew = true;\n" - " this._bindVars = {};\n" - " this._id = null;\n" - " return true;\n" - " }\n" - " \n" - " return false;\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief bind a parameter to the statement\n" - "///\n" - "/// This function can be called multiple times, once for each bind parameter.\n" - "/// All bind parameters will be transferred to the server in one go when \n" - "/// execute() is called.\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoStoredStatement.prototype.bind = function (key, value) {\n" - " if (typeof(key) != \"string\") {\n" - " throw \"bind parameter name must be a string\";\n" - " }\n" - "\n" - " if (this._bindVars[key] != undefined) {\n" - " throw \"redeclaration of bind parameter\";\n" - " }\n" - "\n" - " this._bindVars[key] = value;\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief set the count flag for the statement\n" - "///\n" - "/// Setting the count flag will make the query instance's cursor return the\n" - "/// total number of result documents. The count flag is not set by default.\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoStoredStatement.prototype.setCount = function (bool) {\n" - " this._doCount = bool ? true : false;\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief set the maximum number of results documents the cursor will return\n" - "/// in a single server roundtrip.\n" - "/// The higher this number is, the less server roundtrips will be made when\n" - "/// iterating over the result documents of a cursor.\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoStoredStatement.prototype.setBatchSize = function (value) {\n" - " if (parseInt(value) > 0) {\n" - " this._batchSize = parseInt(value);\n" - " }\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief execute the query\n" - "///\n" - "/// Invoking execute() will transfer the query and all bind parameters to the\n" - "/// server. It will return a cursor with the query results in case of success.\n" - "/// In case of an error, the error will be printed\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoStoredStatement.prototype.execute = function () {\n" - " if (this._isNew) {\n" - " this.save();\n" - " }\n" - " \n" - " var body = {\n" - " \"name\" : this.document.name,\n" - " \"count\" : this._doCount,\n" - " \"bindVars\" : this._bindVars,\n" - " \"_id\" : this._id\n" - " }\n" - "\n" - " if (this._batchSize) {\n" - " body[\"batchSize\"] = this._batchSize;\n" - " }\n" - " \n" - " var requestResult = this._database._connection.post(\"/_api/cursor\", JSON.stringify(body));\n" - " \n" - " if (isErrorResult(requestResult)) {\n" - " return undefined;\n" - " }\n" - "\n" - " return new AvocadoQueryCursor(this._database, requestResult);\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief print the help for AvocadoStoredStatement\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoStoredStatement.prototype._help = function () {\n" - " print(helpAvocadoStoredStatement);\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief return a string representation of the stored statement\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoStoredStatement.prototype.toString = function () { \n" - " return getIdString(this, \"AvocadoStoredStatement\");\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief factory method to create a new stored statement\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoDatabase.prototype._createStoredStatement = function (data) { \n" - " return new AvocadoStoredStatement(this, data);\n" - "}\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief factory method to retrieve an existing stored statement\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "AvocadoDatabase.prototype._getStoredStatement = function (data) { \n" - " return new AvocadoStoredStatement(this, data);\n" - "}\n" - "\n" - "*/\n" "// -----------------------------------------------------------------------------\n" "// --SECTION-- AvocadoStatement\n" "// -----------------------------------------------------------------------------\n" @@ -978,7 +699,7 @@ static string JS_client_client = "/// in a single server roundtrip.\n" "////////////////////////////////////////////////////////////////////////////////\n" "\n" - "AvocadoStatement.prototype.getMax = function () {\n" + "AvocadoStatement.prototype.getBatchSize = function () {\n" " return this._batchSize;\n" "}\n" "\n" @@ -1127,7 +848,7 @@ static string JS_client_client = "' > st.setCount(); set count flag (return number of ' + \"\\n\" +\n" "' results in \"count\" attribute) ' + \"\\n\" +\n" "'Get query options: ' + \"\\n\" +\n" - "' > st.getMax(); return the max. number of results ' + \"\\n\" +\n" + "' > st.setBatchSize(); return the max. number of results ' + \"\\n\" +\n" "' to be transferred per roundtrip ' + \"\\n\" +\n" "' > st.getCount(); return count flag (return number of' + \"\\n\" +\n" "' results in \"count\" attribute) ' + \"\\n\" +\n" @@ -1206,7 +927,7 @@ static string JS_client_client = "' to be transferred per roundtrip ' + \"\\n\" +\n" "' setCount(); set count flag (return number of ' + \"\\n\" +\n" "' results in \"count\" attribute) ' + \"\\n\" +\n" - "' getMax(); return max. number of results ' + \"\\n\" +\n" + "' getBatchSize(); return max. number of results ' + \"\\n\" +\n" "' to be transferred per roundtrip ' + \"\\n\" +\n" "' getCount(); return count flag (return number of' + \"\\n\" +\n" "' results in \"count\" attribute) ' + \"\\n\" +\n" @@ -1222,32 +943,7 @@ static string JS_client_client = "' > st.bind(\"b\", \"world\"); ' + \"\\n\" +\n" "' > c = st.execute(); ' + \"\\n\" +\n" "' > print(c.elements()); ';\n" - "/*\n" - "helpAvocadoStoredStatement = \n" - "getHeadline(\"AvocadoQueryTemplate help\") +\n" - "'AvocadoQueryTemplate constructor: ' + \"\\n\" +\n" - "' > qt1 = db._createQueryTemplate(\"select ...\"); simple query ' + \"\\n\" +\n" - "' > qt2 = db._createQueryTemplate( complex query ' + \"\\n\" +\n" - "' {query:\"select...\", ' + \"\\n\" +\n" - "' name:\"qname\", ' + \"\\n\" +\n" - "' collection:\"q\" ' + \"\\n\" +\n" - "' ... } ' + \"\\n\" +\n" - "'Functions: ' + \"\\n\" +\n" - "' update(); update query template ' + \"\\n\" +\n" - "' delete(); delete query template by id ' + \"\\n\" +\n" - "' getInstance(); get a query instance ' + \"\\n\" +\n" - "' returns: AvocadoQueryInstance' + \"\\n\" +\n" - "' _help(); this help ' + \"\\n\" +\n" - "'Attributes: ' + \"\\n\" +\n" - "' _database database object ' + \"\\n\" +\n" - "' _id template id ' + \"\\n\" +\n" - "' name collection name ' + \"\\n\" +\n" - "'Example: ' + \"\\n\" +\n" - "' > qt1 = db._getQueryTemplate(\"4334:2334\"); ' + \"\\n\" +\n" - "' > qt1.update(\"select a from collA a\"); ' + \"\\n\" +\n" - "' > qi1 = qt1.getInstance(); ' + \"\\n\" +\n" - "' > qt1.delete(\"4334:2334\"); ';\n" - "*/\n" + "\n" "helpExtended = \n" "getHeadline(\"More help\") +\n" "'Pager: ' + \"\\n\" +\n" diff --git a/js/common/bootstrap/errors.js b/js/common/bootstrap/errors.js index 0dc5ca094d..ece2442204 100644 --- a/js/common/bootstrap/errors.js +++ b/js/common/bootstrap/errors.js @@ -16,7 +16,9 @@ ModuleCache["/internal"].exports.errors = { "ERROR_DEAD_PID" : { "code" : 8, "message" : "dead process identifier" }, "ERROR_NOT_IMPLEMENTED" : { "code" : 9, "message" : "not implemented" }, "ERROR_HTTP_BAD_PARAMETER" : { "code" : 400, "message" : "bad parameter" }, + "ERROR_HTTP_NOT_FOUND" : { "code" : 404, "message" : "not found" }, "ERROR_HTTP_METHOD_NOT_ALLOWED" : { "code" : 405, "message" : "method not supported" }, + "ERROR_HTTP_SERVER_ERROR" : { "code" : 500, "message" : "internal server error" }, "ERROR_HTTP_CORRUPTED_JSON" : { "code" : 600, "message" : "invalid JSON object" }, "ERROR_HTTP_SUPERFLUOUS_SUFFICES" : { "code" : 601, "message" : "superfluous URL suffices" }, "ERROR_AVOCADO_ILLEGAL_STATE" : { "code" : 1000, "message" : "illegal state" }, diff --git a/js/common/bootstrap/js-errors.h b/js/common/bootstrap/js-errors.h index c5cdabb9fb..8fc31524cd 100644 --- a/js/common/bootstrap/js-errors.h +++ b/js/common/bootstrap/js-errors.h @@ -17,7 +17,9 @@ static string JS_common_bootstrap_errors = " \"ERROR_DEAD_PID\" : { \"code\" : 8, \"message\" : \"dead process identifier\" }, \n" " \"ERROR_NOT_IMPLEMENTED\" : { \"code\" : 9, \"message\" : \"not implemented\" }, \n" " \"ERROR_HTTP_BAD_PARAMETER\" : { \"code\" : 400, \"message\" : \"bad parameter\" }, \n" + " \"ERROR_HTTP_NOT_FOUND\" : { \"code\" : 404, \"message\" : \"not found\" }, \n" " \"ERROR_HTTP_METHOD_NOT_ALLOWED\" : { \"code\" : 405, \"message\" : \"method not supported\" }, \n" + " \"ERROR_HTTP_SERVER_ERROR\" : { \"code\" : 500, \"message\" : \"internal server error\" }, \n" " \"ERROR_HTTP_CORRUPTED_JSON\" : { \"code\" : 600, \"message\" : \"invalid JSON object\" }, \n" " \"ERROR_HTTP_SUPERFLUOUS_SUFFICES\" : { \"code\" : 601, \"message\" : \"superfluous URL suffices\" }, \n" " \"ERROR_AVOCADO_ILLEGAL_STATE\" : { \"code\" : 1000, \"message\" : \"illegal state\" }, \n"