xoopit-cloud_query 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 5
@@ -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>
@@ -0,0 +1,11 @@
1
+ require 'spec'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'cloud_query'
6
+ require 'parsedate'
7
+ require 'pp'
8
+
9
+ Spec::Runner.configure do |config|
10
+
11
+ end
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