xoopit-cloud_query 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.markdown +80 -0
- data/Rakefile +80 -0
- data/VERSION.yml +4 -0
- data/lib/cloud_query.rb +43 -0
- data/lib/cloudquery.rb +1 -0
- data/spec/cloudquery_spec.rb +437 -0
- data/spec/example_schema.xml +26 -0
- data/spec/spec_helper.rb +11 -0
- metadata +93 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 nb.io
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
cloudquery
|
2
|
+
==========
|
3
|
+
|
4
|
+
Client for Xoopit's cloudquery API
|
5
|
+
|
6
|
+
Install
|
7
|
+
-------
|
8
|
+
|
9
|
+
Depends on `json`, `rack`, and `taf2-curb`. The install below should take
|
10
|
+
care of it. If not, `sudo gem install json rack taf2-curb` will do it.
|
11
|
+
|
12
|
+
Be sure you've run `gem sources -a http://gems.github.com` once on your system. Then:
|
13
|
+
|
14
|
+
sudo gem install xoopit-cloudquery
|
15
|
+
|
16
|
+
Simple contacts application example
|
17
|
+
-----------------------------------
|
18
|
+
|
19
|
+
> require 'rubygems'
|
20
|
+
=> true
|
21
|
+
> require 'cloudquery'
|
22
|
+
=> true
|
23
|
+
> include Cloudquery
|
24
|
+
=> Object
|
25
|
+
> secret = Client.get_secret(<account_name>, <password>)
|
26
|
+
=> "your secret appears here"
|
27
|
+
> c = Client.new(:account => '<account_name>', :secret => secret)
|
28
|
+
=> #<Cloudquery::Client:0x10b1b24 @secure=true, @secret="your secret appears here", @account="<account_name>", @document_id_method=nil>
|
29
|
+
> c.add_indexes('superheroes')
|
30
|
+
=> {"result"=>["kMzzzybpqpY"], "size"=>1, "STATUS"=>200}
|
31
|
+
> c.add_schema(File.open('simple.contact.xml'))
|
32
|
+
=> {"result"=>["ubKme0EX3H2ud7VhBU7qngk3........."], "size"=>1, "STATUS"=>201}
|
33
|
+
> doc = {
|
34
|
+
'simple.contact.name' => 'Steve Rogers',
|
35
|
+
'simple.contact.email' => ['steve.rogers@example.com','captain.america@marvel.com'],
|
36
|
+
'simple.contact.telephone' => ['555-555-5555','123-456-6789'],
|
37
|
+
'simple.contact.address' => ['Lower East Side, NY NY'],
|
38
|
+
'simple.contact.birthday' => Date.parse('July 4, 1917'),
|
39
|
+
'simple.contact.note' => 'Captain America!',
|
40
|
+
}
|
41
|
+
=> {"simple.contact.birthday"=>#<Date: 4842827/2,0,2299161>, "simple.contact.address"=>["Lower East Side, NY NY"], "simple.contact.telephone"=>["555-555-5555", "123-456-6789"], "simple.contact.note"=>"Captain America!", "simple.contact.email"=>["steve.rogers@example.com", "captain.america@marvel.com"], "simple.contact.name"=>"Steve Rogers"}
|
42
|
+
> c.add_documents('superheroes', doc, 'simple.contact')
|
43
|
+
=> {"result"=>["nDLCNLPo3oHtxANzG4YBn5kMzzzybpqpY"], "size"=>1, "STATUS"=>201}
|
44
|
+
> docs = [
|
45
|
+
{
|
46
|
+
'simple.contact.name' => 'Clark Kent',
|
47
|
+
'simple.contact.email' => ['clark.kent@example.com','superman@dc.com'],
|
48
|
+
'simple.contact.telephone' => ['555-123-1234','555-456-6789'],
|
49
|
+
'simple.contact.address' => ['344 Clinton St., Apt. #3B, Metropolis', 'The Fortess of Solitude, North Pole'],
|
50
|
+
'simple.contact.birthday' => Date.parse('June 18, 1938'),
|
51
|
+
'simple.contact.note' => 'Superhuman strength, speed, stamina, durability, senses, intelligence, regeneration, and longevity; super breath, heat vision, x-ray vision and flight. Member of the justice league.'
|
52
|
+
},
|
53
|
+
{
|
54
|
+
'simple.contact.name' => 'Bruce Wayne',
|
55
|
+
'simple.contact.email' => ['bruce.wayne@example.com','batman@dc.com'],
|
56
|
+
'simple.contact.telephone' => ['555-123-6666','555-456-6666'],
|
57
|
+
'simple.contact.address' => ['1007 Mountain Drive, Gotham', 'The Batcave, Gotham'],
|
58
|
+
'simple.contact.birthday' => Date.parse('February 19, 1939'),
|
59
|
+
'simple.contact.note' => 'Sidekick is Robin. Has problems with the Joker. Member of e justice league.'
|
60
|
+
}
|
61
|
+
]
|
62
|
+
> c.add_documents('superheroes', docs, 'simple.contact')
|
63
|
+
=> {"result"=>["lQgByVSvJk1skHtKpMYX40kMzzzybpqpY", "weJF4uDPJrlvrETTJQNibFkMzzzybpqpY"], "size"=>2, "STATUS"=>201}
|
64
|
+
> c.count_documents('superheroes', '*', 'simple.contact')
|
65
|
+
=> {"result"=>3, "matches"=>3, "STATUS"=>200}
|
66
|
+
> c.get_documents('superheroes', '*', {:fields => 'simple.contact.name'}, 'simple.contact')
|
67
|
+
=> {"result"=>[{"simple.contact.name"=>"Steve Rogers"}, {"simple.contact.name"=>"Clark Kent"}, {"simple.contact.name"=>"Bruce Wayne"}], "matches"=>3, "size"=>3, "STATUS"=>200}
|
68
|
+
> c.get_documents('superheroes', 'name:Steve', {:fields => 'simple.contact.name'}, 'simple.contact')
|
69
|
+
=> {"result"=>[{"simple.contact.name"=>"Steve Rogers"}], "matches"=>1, "size"=>1, "STATUS"=>200}
|
70
|
+
> c.get_documents('superheroes', ':@:justice', {:fields => 'simple.contact.name'}, 'simple.contact')
|
71
|
+
=> {"result"=>[{"simple.contact.name"=>"Clark Kent"}, {"simple.contact.name"=>"Bruce Wayne"}], "matches"=>2, "size"=>2, "STATUS"=>200}
|
72
|
+
> c.modify_documents('superheroes', 'name:steve', {'simple.contact.note' => 'His name is STEVE!'}, 'simple.contact')
|
73
|
+
=> {"result"=>["nDLCNLPo3oHtxANzG4YBn5kMzzzybpqpY"], "matches"=>1, "size"=>1, "STATUS"=>200}
|
74
|
+
> c.delete_documents('superheroes', 'name:steve', 'simple.contact') => {"result"=>["nDLCNLPo3oHtxANzG4YBn5kMzzzybpqpY"], "matches"=>2, "size"=>1, "STATUS"=>200}
|
75
|
+
|
76
|
+
|
77
|
+
Copyright
|
78
|
+
---------
|
79
|
+
|
80
|
+
Copyright (c) 2009 nb.io, LLC and Xoopit, Inc. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |gem|
|
6
|
+
gem.name = "cloud_query"
|
7
|
+
gem.summary = "Client for Xoopit's CloudQuery API"
|
8
|
+
gem.email = "us@nb.io"
|
9
|
+
gem.homepage = "http://github.com/xoopit/cloudquery_ruby"
|
10
|
+
gem.description = "Client for Xoopit's CloudQuery API"
|
11
|
+
gem.authors = ["Cameron Walters", "nb.io"]
|
12
|
+
gem.files = FileList["[A-Z]*", "{lib,spec}/**/*"]
|
13
|
+
# gem.rubyforge_project = "cloudquery"
|
14
|
+
gem.add_dependency('rack', ">= 1.0")
|
15
|
+
gem.add_dependency('json', ">= 1.1.4")
|
16
|
+
gem.add_dependency('taf2-curb', ">= 0.2.8.0")
|
17
|
+
|
18
|
+
|
19
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler not available. Install it with: sudo gem install jeweler"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'spec/rake/spectask'
|
26
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
27
|
+
spec.libs << 'lib' << 'spec'
|
28
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
29
|
+
spec.spec_opts << "-c"
|
30
|
+
end
|
31
|
+
|
32
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
33
|
+
spec.libs << 'lib' << 'spec'
|
34
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
35
|
+
spec.rcov = true
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rake/rdoctask'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
if File.exist?('VERSION.yml')
|
44
|
+
config = YAML.load(File.read('VERSION.yml'))
|
45
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
46
|
+
else
|
47
|
+
version = ""
|
48
|
+
end
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "CloudQuery #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
55
|
+
|
56
|
+
# begin
|
57
|
+
# require 'rake/contrib/sshpublisher'
|
58
|
+
# namespace :rubyforge do
|
59
|
+
#
|
60
|
+
# desc "Release gem and RDoc documentation to RubyForge"
|
61
|
+
# task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
62
|
+
#
|
63
|
+
# namespace :release do
|
64
|
+
# desc "Publish RDoc to RubyForge."
|
65
|
+
# task :docs => [:rdoc] do
|
66
|
+
# config = YAML.load(
|
67
|
+
# File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
68
|
+
# )
|
69
|
+
#
|
70
|
+
# host = "#{config['username']}@rubyforge.org"
|
71
|
+
# remote_dir = "/var/www/gforge-projects/cloudquery/"
|
72
|
+
# local_dir = 'rdoc'
|
73
|
+
#
|
74
|
+
# Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
# end
|
78
|
+
# rescue LoadError
|
79
|
+
# puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
80
|
+
# end
|
data/VERSION.yml
ADDED
data/lib/cloud_query.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "uri"
|
3
|
+
require "digest/sha1"
|
4
|
+
require "base64"
|
5
|
+
require "rack/utils"
|
6
|
+
require "curl"
|
7
|
+
require "json"
|
8
|
+
require 'nokogiri'
|
9
|
+
|
10
|
+
require 'cloud_query/client'
|
11
|
+
require 'cloud_query/crypto'
|
12
|
+
require 'cloud_query/field'
|
13
|
+
require 'cloud_query/request'
|
14
|
+
require 'cloud_query/schema'
|
15
|
+
|
16
|
+
module CloudQuery
|
17
|
+
SCHEME = "https".freeze
|
18
|
+
HOST = "api.xoopit.com".freeze
|
19
|
+
PATH = "/v0".freeze
|
20
|
+
|
21
|
+
API_PATHS = {
|
22
|
+
:account => "account".freeze,
|
23
|
+
:schema => "schema".freeze,
|
24
|
+
:indexes => "i".freeze,
|
25
|
+
:documents => "i".freeze,
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
# Standard Content-Types for requests
|
29
|
+
CONTENT_TYPES = {
|
30
|
+
:json => 'application/json;charset=utf-8'.freeze,
|
31
|
+
:form => 'application/x-www-form-urlencoded'.freeze,
|
32
|
+
:xml => 'application/xml;charset=utf-8'.freeze,
|
33
|
+
}.freeze
|
34
|
+
|
35
|
+
SIGNING_METHOD = "SHA1".freeze
|
36
|
+
COOKIE_JAR = (ENV["COOKIE_JAR"] || ".cookies.lwp").freeze
|
37
|
+
end
|
38
|
+
|
39
|
+
class Time
|
40
|
+
def to_i_with_milliseconds
|
41
|
+
(to_f * 1000).to_i
|
42
|
+
end
|
43
|
+
end
|
data/lib/cloudquery.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'cloud_query'
|
@@ -0,0 +1,437 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if ENV["TEST_REAL_HTTP"]
|
4
|
+
# Create a config.yml file containing the following:
|
5
|
+
# :account: <your account name>
|
6
|
+
# :secret: <your secret>
|
7
|
+
# then run the specs with TEST_REAL_HTTP=true
|
8
|
+
describe "CloudQuery account" do
|
9
|
+
before(:each) do
|
10
|
+
@config = YAML.load(File.read('config.yml'))
|
11
|
+
@client = CloudQuery::Client.new(@config)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "gets your account information from the server" do
|
15
|
+
response = @client.get_account
|
16
|
+
response['STATUS'].should be_between(200, 299)
|
17
|
+
|
18
|
+
account = response["result"]
|
19
|
+
account["secret"].should == @config[:secret]
|
20
|
+
|
21
|
+
account.should have_key("name")
|
22
|
+
account["name"].should == @config[:account]
|
23
|
+
|
24
|
+
account.should have_key("preferences")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "updates your account on the server" do
|
28
|
+
account = @client.get_account["result"]
|
29
|
+
response = @client.update_account(account)
|
30
|
+
response['STATUS'].should be_between(200, 299)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "adds a schema to your account on the server" do
|
34
|
+
response = @client.add_schema(File.open('spec/example_schema.xml'))
|
35
|
+
response['STATUS'].should be_between(200, 299)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "gets the schemas for your account from the server" do
|
39
|
+
response = @client.get_schemas
|
40
|
+
response['STATUS'].should be_between(200, 299)
|
41
|
+
response['result'].should be_an_instance_of(Array)
|
42
|
+
response['result'].should have_at_least(1).item
|
43
|
+
end
|
44
|
+
|
45
|
+
it "deletes a schema from your account on the server" do
|
46
|
+
response = @client.delete_schema("spec.example")
|
47
|
+
response['STATUS'].should be_between(200, 299)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "adds a single index to your account on the server" do
|
51
|
+
response = @client.add_indexes('spec_index')
|
52
|
+
response['STATUS'].should be_between(200, 299)
|
53
|
+
response['result'].should be_an_instance_of(Array)
|
54
|
+
response['result'].should have(1).item
|
55
|
+
end
|
56
|
+
|
57
|
+
it "adds multiple indexes to your account on the server" do
|
58
|
+
response = @client.add_indexes %w( spec_index_1 spec_index_2 spec_index_3 )
|
59
|
+
response['STATUS'].should be_between(200, 299)
|
60
|
+
response['result'].should be_an_instance_of(Array)
|
61
|
+
response['result'].should have(3).items
|
62
|
+
end
|
63
|
+
|
64
|
+
it "gets the indexes for your account from the server" do
|
65
|
+
response = @client.get_indexes
|
66
|
+
response['STATUS'].should be_between(200, 299)
|
67
|
+
response['result'].should be_an_instance_of(Array)
|
68
|
+
response['result'].should have_at_least(4).items
|
69
|
+
end
|
70
|
+
|
71
|
+
it "deletes a single index from your account on the server" do
|
72
|
+
response = @client.delete_indexes('spec_index')
|
73
|
+
response['STATUS'].should be_between(200, 299)
|
74
|
+
response['result'].should be_an_instance_of(Array)
|
75
|
+
response['result'].should have(1).item
|
76
|
+
end
|
77
|
+
|
78
|
+
it "deletes multiple indexes from your account on the server" do
|
79
|
+
response = @client.delete_indexes %w( spec_index_1 spec_index_2 spec_index_3 )
|
80
|
+
response['STATUS'].should be_between(200, 299)
|
81
|
+
response['result'].should be_an_instance_of(Array)
|
82
|
+
response['result'].should have(3).items
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "document support" do
|
86
|
+
def valid_document
|
87
|
+
{
|
88
|
+
'spec.example.name' => 'Steve Rogers',
|
89
|
+
'spec.example.email' => ['steve.rogers@example.com','captain.america@marvel.com'],
|
90
|
+
'spec.example.telephone' => ['555-555-5555','123-456-6789'],
|
91
|
+
'spec.example.address' => ['Lower East Side, NY NY'],
|
92
|
+
'spec.example.birthday' => ParseDate.parsedate('July 4, 1917'),
|
93
|
+
'spec.example.note' => 'Captain America!',
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_valid_document(index=nil)
|
98
|
+
index ||= 'spec_index'
|
99
|
+
response = @client.add_documents(index, valid_document, 'spec.example')
|
100
|
+
response['result'].first
|
101
|
+
end
|
102
|
+
|
103
|
+
before(:each) do
|
104
|
+
@client.add_indexes('spec_index')
|
105
|
+
@client.add_schema(File.open('spec/example_schema.xml'))
|
106
|
+
end
|
107
|
+
|
108
|
+
after(:each) do
|
109
|
+
@client.delete_schema("spec.example")
|
110
|
+
@client.delete_indexes('spec_index')
|
111
|
+
end
|
112
|
+
|
113
|
+
it "adds a document to an index on the server" do
|
114
|
+
response = @client.add_documents('spec_index', valid_document, 'spec.example')
|
115
|
+
response['STATUS'].should == 201
|
116
|
+
response['result'].should have(1).item
|
117
|
+
end
|
118
|
+
|
119
|
+
it "adds multiple documents to an index on the server" do
|
120
|
+
documents = [
|
121
|
+
valid_document,
|
122
|
+
{
|
123
|
+
'spec.example.name' => 'Clark Kent',
|
124
|
+
'spec.example.email' => ['clark.kent@example.com','superman@dc.com'],
|
125
|
+
'spec.example.telephone' => ['555-123-1234', '555-456-6789'],
|
126
|
+
'spec.example.address' =>
|
127
|
+
['344 Clinton St., Apt. #3B, Metropolis', 'The Fortess of Solitude, North Pole'],
|
128
|
+
'spec.example.birthday' => ParseDate.parsedate('June 18, 1938'),
|
129
|
+
'spec.example.note' =>
|
130
|
+
'Superhuman strength, speed, stamina, durability, senses, intelligence, regeneration, and longevity; super breath, heat vision, x-ray vision and flight. Member of the justice league.',
|
131
|
+
},
|
132
|
+
{
|
133
|
+
'spec.example.name' => 'Bruce Wayne',
|
134
|
+
'spec.example.email' => ['bruce.wayne@example.com','batman@dc.com'],
|
135
|
+
'spec.example.telephone' => ['555-123-6666', '555-456-6666'],
|
136
|
+
'spec.example.address' =>
|
137
|
+
['1007 Mountain Drive, Gotham', 'The Batcave, Gotham'],
|
138
|
+
'spec.example.birthday' => ParseDate.parsedate('February 19, 1939'),
|
139
|
+
'spec.example.note' =>
|
140
|
+
'Sidekick is Robin. Has problems with the Joker. Member of the justice league.',
|
141
|
+
},
|
142
|
+
]
|
143
|
+
|
144
|
+
response = @client.add_documents('spec_index', documents, 'spec.example')
|
145
|
+
response['STATUS'].should == 201
|
146
|
+
response['result'].should have(3).items
|
147
|
+
end
|
148
|
+
|
149
|
+
it "updates a document on the server" do
|
150
|
+
doc = valid_document
|
151
|
+
doc['#.#'] = add_valid_document
|
152
|
+
doc['spec.example.note'] = "Document modified!"
|
153
|
+
|
154
|
+
response = @client.update_documents('spec_index', doc, 'spec.example')
|
155
|
+
response['STATUS'].should == 200
|
156
|
+
response['result'].should have(1).item
|
157
|
+
end
|
158
|
+
|
159
|
+
it "modifies documents on the server" do
|
160
|
+
add_valid_document
|
161
|
+
mods = {'spec.example.note' => 'Document modified!'}
|
162
|
+
response = @client.modify_documents(
|
163
|
+
"spec_index",
|
164
|
+
"name:#{valid_document['spec.example.name']}",
|
165
|
+
mods,
|
166
|
+
"spec.example"
|
167
|
+
)
|
168
|
+
response['STATUS'].should == 200 # OK
|
169
|
+
response['result'].should have(1).item
|
170
|
+
end
|
171
|
+
|
172
|
+
it "gets a document from the server" do
|
173
|
+
add_valid_document
|
174
|
+
response = @client.get_documents('spec_index', nil, {}, 'spec.example')
|
175
|
+
response['STATUS'].should == 200
|
176
|
+
response['result'].should have(1).item
|
177
|
+
stored_document = response['result'].first
|
178
|
+
valid_document.each { |key, value| stored_document.should have_key(key) }
|
179
|
+
end
|
180
|
+
|
181
|
+
it "gets a document from multiple indexes on the server" do
|
182
|
+
@client.add_indexes('spec_index_2')
|
183
|
+
@client.delete_documents(nil, nil)
|
184
|
+
add_valid_document
|
185
|
+
add_valid_document('spec_index_2')
|
186
|
+
|
187
|
+
response = @client.get_documents(nil, nil, {}, 'spec.example')
|
188
|
+
response['STATUS'].should == 200
|
189
|
+
response['result'].should have(2).items
|
190
|
+
stored_document_1 = response['result'].first
|
191
|
+
stored_document_2 = response['result'].last
|
192
|
+
|
193
|
+
valid_document.each { |key, value| stored_document_1.should have_key(key) }
|
194
|
+
valid_document.each { |key, value| stored_document_2.should have_key(key) }
|
195
|
+
|
196
|
+
@client.delete_indexes('spec_index_2')
|
197
|
+
end
|
198
|
+
|
199
|
+
it "counts documents from the server" do
|
200
|
+
@client.delete_documents(nil, nil)
|
201
|
+
add_valid_document
|
202
|
+
|
203
|
+
response = @client.count_documents('spec_index', '*', 'spec.example')
|
204
|
+
response['STATUS'].should == 200
|
205
|
+
response['result'].should == 1
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe CloudQuery::Client do
|
212
|
+
before(:each) do
|
213
|
+
@valid_options = {
|
214
|
+
:account => 'account',
|
215
|
+
:secret => 'secret'
|
216
|
+
}
|
217
|
+
end
|
218
|
+
|
219
|
+
def client(options={})
|
220
|
+
return @client if defined?(@client)
|
221
|
+
@client = CloudQuery::Client.new(@valid_options.merge(options))
|
222
|
+
@client.stub!(:execute_request)
|
223
|
+
@client
|
224
|
+
end
|
225
|
+
|
226
|
+
it "instantiates when passed valid arguments" do
|
227
|
+
lambda { client }.should_not raise_error
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
describe CloudQuery::Request do
|
233
|
+
before(:each) do
|
234
|
+
@valid_options = {
|
235
|
+
:scheme => 'http',
|
236
|
+
:host => 'example.com',
|
237
|
+
:path => '/super/duper/path',
|
238
|
+
}
|
239
|
+
end
|
240
|
+
|
241
|
+
def request(additional_options={})
|
242
|
+
return @request if defined?(@request)
|
243
|
+
@request = CloudQuery::Request.new(@valid_options.merge(additional_options))
|
244
|
+
end
|
245
|
+
|
246
|
+
it "instantiates with valid options" do
|
247
|
+
lambda { request }.should_not raise_error
|
248
|
+
end
|
249
|
+
|
250
|
+
describe "request_uri" do
|
251
|
+
describe "without an account or secret" do
|
252
|
+
it "appends the query_str to the path after '?'" do
|
253
|
+
request.should_receive(:query_str).at_least(:once).and_return("query=string&more=params")
|
254
|
+
request.request_uri.should == "#{request.path}?#{request.send(:query_str)}"
|
255
|
+
end
|
256
|
+
|
257
|
+
it "doesn't append a '?' when query_str is empty" do
|
258
|
+
request.should_receive(:query_str).at_least(:once).and_return("")
|
259
|
+
request.request_uri.should == request.path
|
260
|
+
request.request_uri.should_not equal(request.path) #ensure we don't accidentally modify request's instance variable
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe "with an account" do
|
265
|
+
it "should append the signature_params" do
|
266
|
+
params = request(:account => 'account').request_uri.sub(/^[^?]+\?/, '').split('&')
|
267
|
+
params.select { |n| n.match(/^x_/) }.should have(4).items
|
268
|
+
end
|
269
|
+
|
270
|
+
describe "and a secret" do
|
271
|
+
it "should append the signature when the secret is provided" do
|
272
|
+
params = request(:account => 'account', :secret => 'secret').request_uri.sub(/^[^?]+\?/, '').split('&')
|
273
|
+
x_params = params.select { |n| n.match(/^x_/) }
|
274
|
+
x_params.should have(5).items
|
275
|
+
x_params.last.should match(/^x_sig=[0-9a-zA-Z\-._%]+/)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe "url" do
|
282
|
+
it "constructs a full URL from the scheme, host, and request_uri" do
|
283
|
+
request.url.should ==
|
284
|
+
"#{request.scheme}://#{request.host}#{request.request_uri}"
|
285
|
+
end
|
286
|
+
|
287
|
+
it "constructs a url using a port override" do
|
288
|
+
request(:port => 8080).url.should ==
|
289
|
+
"#{request.scheme}://#{request.host}:8080#{request.request_uri}"
|
290
|
+
end
|
291
|
+
|
292
|
+
it "constructs a url using a path override" do
|
293
|
+
request(:path => '/another/path').url.should ==
|
294
|
+
"#{request.scheme}://#{request.host}#{request.request_uri}"
|
295
|
+
end
|
296
|
+
|
297
|
+
it "constructs a url with default query parameters" do
|
298
|
+
request(:params => {'these' => 'params'}).url.should ==
|
299
|
+
"#{request.scheme}://#{request.host}#{request.request_uri}"
|
300
|
+
request.url.should match(/these=params$/)
|
301
|
+
end
|
302
|
+
|
303
|
+
describe "without an account or secret" do
|
304
|
+
it "does not append the x_<params>" do
|
305
|
+
request.url.should_not match(/x_/)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
describe "with an account" do
|
310
|
+
it "appends the signature params" do
|
311
|
+
url = request(:account => 'account').url
|
312
|
+
query = Rack::Utils.parse_query(url.split('?').last)
|
313
|
+
request.send(:signature_params).keys.each do |param_name|
|
314
|
+
query.should have_key(param_name)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
describe "and a secret" do
|
319
|
+
it "appends the signature params and x_sig with the signature" do
|
320
|
+
url = request(:account => 'account', :secret => 'secret').url
|
321
|
+
query = Rack::Utils.parse_query(url.split('?').last)
|
322
|
+
signature_params = request.send(:signature_params).keys
|
323
|
+
signature_params.each do |param_name|
|
324
|
+
query.should have_key(param_name)
|
325
|
+
end
|
326
|
+
query.should have_key('x_sig')
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
describe "private methods" do
|
333
|
+
|
334
|
+
describe "append_signature" do
|
335
|
+
it "should append the signature as the x_sig parameter at the end of the query string" do
|
336
|
+
url = 'http://example.com/path?query=string'
|
337
|
+
signed_url = request.send(:append_signature, url, 'secret')
|
338
|
+
signed_url.should match(/^#{url.sub(/\?/, '\\?')}/)
|
339
|
+
signed_url.should match(/x_sig=[-\w]+(?:%3D)*$/)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
describe "signature_params" do
|
344
|
+
describe "without an account present" do
|
345
|
+
it "should return an empty hash" do
|
346
|
+
request.send(:signature_params).should == {}
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
describe "with an account present" do
|
351
|
+
before(:each) do
|
352
|
+
@params = request(:account => 'account').send(:signature_params)
|
353
|
+
end
|
354
|
+
|
355
|
+
it "should return a hash with the x_name parameter with the account name" do
|
356
|
+
@params.should have_key('x_name')
|
357
|
+
@params['x_name'].should == 'account'
|
358
|
+
end
|
359
|
+
|
360
|
+
it "should return a hash with the x_time parameter with the current milliseconds since epoch" do
|
361
|
+
@params.should have_key('x_time')
|
362
|
+
@params['x_time'].should be_close(Time.now.to_i_with_milliseconds, 100)
|
363
|
+
end
|
364
|
+
|
365
|
+
it "should return a hash with the x_nonce parameter of the format \d+.\d+" do
|
366
|
+
@params.should have_key('x_nonce')
|
367
|
+
@params['x_nonce'].should match(/^\d+.\d+$/)
|
368
|
+
end
|
369
|
+
|
370
|
+
it "should return a hash with the x_method parameter with the signing method name" do
|
371
|
+
@params.should have_key('x_method')
|
372
|
+
@params['x_method'].should == CloudQuery::SIGNING_METHOD
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
describe "query_str" do
|
378
|
+
it "builds a query string from the request params" do
|
379
|
+
request(:params => {'these' => 'params'})
|
380
|
+
request.send(:query_str).should == 'these=params'
|
381
|
+
end
|
382
|
+
|
383
|
+
it "url-encodes params with non alphanumeric characters (outside [ a-zA-Z0-9-._])" do
|
384
|
+
request(:params => {'weird' => 'values=here'})
|
385
|
+
request.send(:query_str).should == 'weird=values%3Dhere'
|
386
|
+
end
|
387
|
+
|
388
|
+
it "returns an empty string when no params are present" do
|
389
|
+
request(:params => {}).send(:query_str) == ""
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
describe "base_uri" do
|
394
|
+
it "returns an http url when the scheme is http" do
|
395
|
+
request(:scheme => 'http').send(:base_uri).should be_an_instance_of(URI::HTTP)
|
396
|
+
end
|
397
|
+
it "returns an https url when the scheme is https" do
|
398
|
+
request(:scheme => 'https').send(:base_uri).should be_an_instance_of(URI::HTTPS)
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
end
|
404
|
+
|
405
|
+
describe CloudQuery::Crypto::Random do
|
406
|
+
describe "nonce generation" do
|
407
|
+
it "generates a nonce with a random number, a dot, and the current time" do
|
408
|
+
nonce = CloudQuery::Crypto::Random.nonce
|
409
|
+
nonce.should match(/^\d+.\d+$/)
|
410
|
+
random_digits, time = nonce.split('.')
|
411
|
+
time.to_i.should be_close(Time.now.to_i, 1)
|
412
|
+
random_digits.should match(/^\d+$/)
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
describe CloudQuery::Crypto::URLSafeSHA1 do
|
418
|
+
describe "sign" do
|
419
|
+
it "takes an arbitrary number of tokens to encrypt" do
|
420
|
+
lambda { CloudQuery::Crypto::URLSafeSHA1.sign }.should_not raise_error
|
421
|
+
lambda { CloudQuery::Crypto::URLSafeSHA1.sign('a') }.should_not raise_error
|
422
|
+
lambda { CloudQuery::Crypto::URLSafeSHA1.sign('a', 'b', 'c') }.should_not raise_error
|
423
|
+
end
|
424
|
+
|
425
|
+
it "produces a url-safe base64 encoded SHA1 digest of tokens" do
|
426
|
+
20.times do
|
427
|
+
token = CloudQuery::Crypto::Random.nonce
|
428
|
+
signature = CloudQuery::Crypto::URLSafeSHA1.sign(token)
|
429
|
+
signature.should_not include('+')
|
430
|
+
signature.should_not include('/')
|
431
|
+
|
432
|
+
b64_digest = Base64.encode64(Digest::SHA1.digest(token)).chomp.tr('+/', '-_')
|
433
|
+
signature.should == b64_digest
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<schema name="spec.example" store="yes">
|
2
|
+
<!-- The full name of the contact -->
|
3
|
+
<field name="name"
|
4
|
+
type="string"
|
5
|
+
analyzer="LCWhitespaceAnalyzer"
|
6
|
+
usage="user" />
|
7
|
+
<!-- The email addresses. A json array: email address -->
|
8
|
+
<field name="email"
|
9
|
+
type="string"
|
10
|
+
usage="user" />
|
11
|
+
<!-- The phone numbers. A json array: phone number -->
|
12
|
+
<field name="telephone"
|
13
|
+
type="string"
|
14
|
+
usage="user" />
|
15
|
+
<!-- The addresses. A json array: address -->
|
16
|
+
<field name="address"
|
17
|
+
type="string"
|
18
|
+
usage="user" />
|
19
|
+
<!-- The birthday of the contact-->
|
20
|
+
<field name="birthday"
|
21
|
+
type="date" />
|
22
|
+
<!-- A note for the contact-->
|
23
|
+
<field name="note"
|
24
|
+
type="text"
|
25
|
+
usage="user" />
|
26
|
+
</schema>
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xoopit-cloud_query
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cameron Walters
|
8
|
+
- nb.io
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-05-04 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rack
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "1.0"
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: json
|
28
|
+
type: :runtime
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.1.4
|
35
|
+
version:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: taf2-curb
|
38
|
+
type: :runtime
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 0.2.8.0
|
45
|
+
version:
|
46
|
+
description: Client for Xoopit's CloudQuery API
|
47
|
+
email: us@nb.io
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- LICENSE
|
54
|
+
- README.markdown
|
55
|
+
files:
|
56
|
+
- LICENSE
|
57
|
+
- README.markdown
|
58
|
+
- Rakefile
|
59
|
+
- VERSION.yml
|
60
|
+
- lib/cloudquery.rb
|
61
|
+
- lib/cloud_query.rb
|
62
|
+
- spec/cloudquery_spec.rb
|
63
|
+
- spec/example_schema.xml
|
64
|
+
- spec/spec_helper.rb
|
65
|
+
has_rdoc: true
|
66
|
+
homepage: http://github.com/nbio/cloudquery
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options:
|
69
|
+
- --charset=UTF-8
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: "0"
|
77
|
+
version:
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: "0"
|
83
|
+
version:
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 1.2.0
|
88
|
+
signing_key:
|
89
|
+
specification_version: 2
|
90
|
+
summary: Client for Xoopit's cloudquery API
|
91
|
+
test_files:
|
92
|
+
- spec/cloudquery_spec.rb
|
93
|
+
- spec/spec_helper.rb
|