contextio 0.5.0 → 1.0.0
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/.document +4 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +5 -0
- data/Gemfile +3 -0
- data/LICENSE.md +20 -0
- data/README.md +62 -22
- data/Rakefile +46 -36
- data/contextio.gemspec +30 -0
- data/lib/contextio.rb +69 -583
- data/lib/contextio/account.rb +132 -0
- data/lib/contextio/account_collection.rb +57 -0
- data/lib/contextio/account_sync_data.rb +22 -0
- data/lib/contextio/api.rb +162 -0
- data/lib/contextio/api/association_helpers.rb +17 -0
- data/lib/contextio/api/resource.rb +230 -0
- data/lib/contextio/api/resource_collection.rb +174 -0
- data/lib/contextio/api/url_builder.rb +153 -0
- data/lib/contextio/body_part.rb +45 -0
- data/lib/contextio/body_part_collection.rb +13 -0
- data/lib/contextio/connect_token.rb +57 -0
- data/lib/contextio/connect_token_collection.rb +44 -0
- data/lib/contextio/contact.rb +43 -0
- data/lib/contextio/contact_collection.rb +21 -0
- data/lib/contextio/email_address.rb +53 -0
- data/lib/contextio/email_address_collection.rb +21 -0
- data/lib/contextio/email_settings.rb +146 -0
- data/lib/contextio/file.rb +92 -0
- data/lib/contextio/file_collection.rb +13 -0
- data/lib/contextio/folder.rb +56 -0
- data/lib/contextio/folder_collection.rb +18 -0
- data/lib/contextio/folder_sync_data.rb +32 -0
- data/lib/contextio/message.rb +96 -0
- data/lib/contextio/message_collection.rb +35 -0
- data/lib/contextio/oauth_provider.rb +29 -0
- data/lib/contextio/oauth_provider_collection.rb +46 -0
- data/lib/contextio/source.rb +55 -0
- data/lib/contextio/source_collection.rb +41 -0
- data/lib/contextio/source_sync_data.rb +23 -0
- data/lib/contextio/thread.rb +15 -0
- data/lib/contextio/thread_collection.rb +25 -0
- data/lib/contextio/version.rb +11 -0
- data/lib/contextio/webhook.rb +39 -0
- data/lib/contextio/webhook_collection.rb +26 -0
- data/spec/config.yml.example +3 -0
- data/spec/contextio/account_collection_spec.rb +78 -0
- data/spec/contextio/account_spec.rb +52 -0
- data/spec/contextio/api/association_helpers_spec.rb +28 -0
- data/spec/contextio/api/resource_collection_spec.rb +286 -0
- data/spec/contextio/api/resource_spec.rb +467 -0
- data/spec/contextio/api/url_builder_spec.rb +78 -0
- data/spec/contextio/api_spec.rb +123 -0
- data/spec/contextio/connect_token_collection_spec.rb +74 -0
- data/spec/contextio/connect_token_spec.rb +58 -0
- data/spec/contextio/email_settings_spec.rb +112 -0
- data/spec/contextio/oauth_provider_collection_spec.rb +36 -0
- data/spec/contextio/oauth_provider_spec.rb +120 -0
- data/spec/contextio/source_collection_spec.rb +57 -0
- data/spec/contextio/source_spec.rb +52 -0
- data/spec/contextio/version_spec.rb +10 -0
- data/spec/contextio_spec.rb +64 -0
- data/spec/spec_helper.rb +17 -0
- metadata +234 -12
- data/README.textile +0 -29
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'contextio/api/url_builder'
|
3
|
+
|
4
|
+
describe ContextIO::API::URLBuilder do
|
5
|
+
let(:api) { double('api') }
|
6
|
+
|
7
|
+
describe "#url_for" do
|
8
|
+
subject { ContextIO::API::URLBuilder.url_for(resource) }
|
9
|
+
|
10
|
+
context "when passed an unregistered class" do
|
11
|
+
let(:resource) { "String isn't registered."}
|
12
|
+
|
13
|
+
it "raises an exception" do
|
14
|
+
expect { subject }.to raise_exception(ContextIO::API::URLBuilder::Error)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when passed a ConnectToken" do
|
19
|
+
context "with an account" do
|
20
|
+
let(:account) { double('account', id: 'account_id') }
|
21
|
+
let(:resource) { ContextIO::ConnectToken.new(api, token: 'token', account: account) }
|
22
|
+
|
23
|
+
it { should eq('accounts/account_id/connect_tokens/token') }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "without an account" do
|
27
|
+
let(:resource) { ContextIO::ConnectToken.new(api, token: 'token', api_attributes: { 'account' => { } }) }
|
28
|
+
|
29
|
+
it { should eq('connect_tokens/token') }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when passed a ConnectTokenCollection" do
|
34
|
+
context "with an account" do
|
35
|
+
let(:account) { double('account', id: 'account_id') }
|
36
|
+
let(:resource) { ContextIO::ConnectTokenCollection.new(api, account: account) }
|
37
|
+
|
38
|
+
it { should eq('accounts/account_id/connect_tokens') }
|
39
|
+
end
|
40
|
+
|
41
|
+
context "wiehout an account" do
|
42
|
+
let(:resource) { ContextIO::ConnectTokenCollection.new(api) }
|
43
|
+
|
44
|
+
it { should eq('connect_tokens') }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when passed an OAuthProvider" do
|
49
|
+
let(:resource) { ContextIO::OAuthProvider.new(api, provider_consumer_key: 'provider_consumer_key') }
|
50
|
+
|
51
|
+
it { should eq('oauth_providers/provider_consumer_key') }
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when passed an OAuthProviderCollection" do
|
55
|
+
let(:resource) { ContextIO::OAuthProviderCollection.new(api) }
|
56
|
+
|
57
|
+
it { should eq('oauth_providers') }
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when passed an EmailSettings" do
|
61
|
+
let(:resource) { ContextIO::EmailSettings.new(api, 'email') }
|
62
|
+
|
63
|
+
it { should eq('discovery') }
|
64
|
+
end
|
65
|
+
|
66
|
+
context "when passed an Account" do
|
67
|
+
let(:resource) { ContextIO::Account.new(api, id: 'account_id') }
|
68
|
+
|
69
|
+
it { should eq('accounts/account_id') }
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when passed an AccountCollection" do
|
73
|
+
let(:resource) { ContextIO::AccountCollection.new(api) }
|
74
|
+
|
75
|
+
it { should eq('accounts') }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'contextio/api'
|
3
|
+
|
4
|
+
describe ContextIO::API do
|
5
|
+
describe ".version" do
|
6
|
+
subject { ContextIO::API }
|
7
|
+
|
8
|
+
it "uses API version 2.0" do
|
9
|
+
expect(subject.version).to eq('2.0')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe ".base_url" do
|
14
|
+
subject { ContextIO::API }
|
15
|
+
|
16
|
+
it "is https://api.context.io" do
|
17
|
+
expect(subject.base_url).to eq('https://api.context.io')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe ".new" do
|
22
|
+
subject { ContextIO::API.new('test_key', 'test_secret') }
|
23
|
+
|
24
|
+
it "takes a key" do
|
25
|
+
expect(subject.key).to eq('test_key')
|
26
|
+
end
|
27
|
+
|
28
|
+
it "takes a secret" do
|
29
|
+
expect(subject.secret).to eq('test_secret')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#path" do
|
34
|
+
context "without params" do
|
35
|
+
subject { ContextIO::API.new(nil, nil).path('test_command') }
|
36
|
+
|
37
|
+
it "puts the command in the path" do
|
38
|
+
expect(subject).to eq('/2.0/test_command')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "with params" do
|
43
|
+
subject { ContextIO::API.new(nil, nil).path('test_command', foo: 1, bar: %w(a b c)) }
|
44
|
+
|
45
|
+
it "URL encodes the params" do
|
46
|
+
expect(subject).to eq('/2.0/test_command?foo=1&bar=a%2Cb%2Cc')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with a full URL" do
|
51
|
+
subject { ContextIO::API.new(nil, nil).path('https://api.context.io/2.0/test_command') }
|
52
|
+
|
53
|
+
it "strips out the command" do
|
54
|
+
expect(subject).to eq('/2.0/test_command')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#request" do
|
60
|
+
subject { ContextIO::API.new(nil, nil).request(:get, 'test') }
|
61
|
+
|
62
|
+
context "with a good response" do
|
63
|
+
before do
|
64
|
+
FakeWeb.register_uri(
|
65
|
+
:get,
|
66
|
+
'https://api.context.io/2.0/test',
|
67
|
+
body: JSON.dump('a' => 'b', 'c' => 'd')
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "parses the JSON response" do
|
72
|
+
expect(subject).to eq('a' => 'b', 'c' => 'd')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "with a bad response that has a body" do
|
77
|
+
before do
|
78
|
+
FakeWeb.register_uri(
|
79
|
+
:get,
|
80
|
+
'https://api.context.io/2.0/test',
|
81
|
+
status: ['400', 'Bad Request'],
|
82
|
+
body: JSON.dump('type' => 'error', 'value' => 'nope')
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "raises an API error with the body message" do
|
87
|
+
expect { subject }.to raise_error(ContextIO::API::Error, 'nope')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "with a bad response that has no body" do
|
92
|
+
before do
|
93
|
+
FakeWeb.register_uri(
|
94
|
+
:get,
|
95
|
+
'https://api.context.io/2.0/test',
|
96
|
+
status: ['400', 'Bad Request']
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "raises an API error with the header message" do
|
101
|
+
expect { subject }.to raise_error(ContextIO::API::Error, 'Bad Request')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe ".url_for" do
|
107
|
+
it "delegates to ContextIO::API::URLBuilder" do
|
108
|
+
ContextIO::API::URLBuilder.should_receive(:url_for).with('foo')
|
109
|
+
|
110
|
+
ContextIO::API.url_for('foo')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#url_for" do
|
115
|
+
subject { ContextIO::API.new('test_key', 'test_secret') }
|
116
|
+
|
117
|
+
it "delegates to the class" do
|
118
|
+
ContextIO::API.should_receive(:url_for).with('foo')
|
119
|
+
|
120
|
+
subject.url_for('foo')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'contextio/connect_token_collection'
|
3
|
+
|
4
|
+
describe ContextIO::ConnectTokenCollection do
|
5
|
+
let(:api) { double('API', url_for: 'url from api') }
|
6
|
+
|
7
|
+
subject { ContextIO::ConnectTokenCollection.new(api) }
|
8
|
+
|
9
|
+
describe "#create" do
|
10
|
+
before do
|
11
|
+
api.stub(:request).with(:post, anything, anything).and_return({ token: '1234' })
|
12
|
+
end
|
13
|
+
|
14
|
+
it "posts to the api" do
|
15
|
+
api.should_receive(:request).with(
|
16
|
+
:post,
|
17
|
+
'url from api',
|
18
|
+
hash_including(callback_url: 'http://callback.com')
|
19
|
+
)
|
20
|
+
|
21
|
+
subject.create('http://callback.com')
|
22
|
+
end
|
23
|
+
|
24
|
+
it "doesn't make any more API calls than it needs to" do
|
25
|
+
api.should_not_receive(:request).with(:get, anything, anything)
|
26
|
+
|
27
|
+
subject.create('http://callback.com')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns a ConnectToken" do
|
31
|
+
expect(subject.create('http://callback.com')).to be_a(ContextIO::ConnectToken)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "takes an optional service level" do
|
35
|
+
api.should_receive(:request).with(
|
36
|
+
anything,
|
37
|
+
anything,
|
38
|
+
hash_including(service_level: 'PRO')
|
39
|
+
)
|
40
|
+
|
41
|
+
subject.create('http://callback.com', service_level: 'PRO')
|
42
|
+
end
|
43
|
+
|
44
|
+
it "takes an optional email" do
|
45
|
+
api.should_receive(:request).with(
|
46
|
+
anything,
|
47
|
+
anything,
|
48
|
+
hash_including(email: 'person@email.com')
|
49
|
+
)
|
50
|
+
|
51
|
+
subject.create('http://callback.com', email: 'person@email.com')
|
52
|
+
end
|
53
|
+
|
54
|
+
it "takes an optional first name" do
|
55
|
+
api.should_receive(:request).with(
|
56
|
+
anything,
|
57
|
+
anything,
|
58
|
+
hash_including(first_name: 'Bruno')
|
59
|
+
)
|
60
|
+
|
61
|
+
subject.create('http://callback.com', first_name: 'Bruno')
|
62
|
+
end
|
63
|
+
|
64
|
+
it "takes an optional last name" do
|
65
|
+
api.should_receive(:request).with(
|
66
|
+
anything,
|
67
|
+
anything,
|
68
|
+
hash_including(last_name: 'Morency')
|
69
|
+
)
|
70
|
+
|
71
|
+
subject.create('http://callback.com', last_name: 'Morency')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'contextio/connect_token'
|
3
|
+
|
4
|
+
describe ContextIO::ConnectToken do
|
5
|
+
let(:api) { double('API') }
|
6
|
+
|
7
|
+
describe ".new" do
|
8
|
+
subject { ContextIO::ConnectToken.new(api, token: '1234', foo: 'bar') }
|
9
|
+
|
10
|
+
it "takes an api handle" do
|
11
|
+
expect(subject.api).to eq(api)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "assigns instance variables for hash arguments" do
|
15
|
+
expect(subject.instance_variable_get('@token')).to eq('1234')
|
16
|
+
expect(subject.instance_variable_get('@foo')).to eq('bar')
|
17
|
+
end
|
18
|
+
|
19
|
+
context "with a token passed in" do
|
20
|
+
it "doesn't raise an error" do
|
21
|
+
expect { ContextIO::ConnectToken.new(api, token: '1234') }.to_not raise_error
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with neither a provider_token nor a resource_url passed in" do
|
26
|
+
it "raises an ArgumentError" do
|
27
|
+
expect { ContextIO::ConnectToken.new(api, foo: 'bar') }.to raise_error(ArgumentError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#token" do
|
33
|
+
context "when input at creation" do
|
34
|
+
subject { ContextIO::ConnectToken.new(api, token: '1234') }
|
35
|
+
|
36
|
+
it "uses the input key" do
|
37
|
+
api.should_not_receive(:request)
|
38
|
+
|
39
|
+
expect(subject.token).to eq('1234')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when not input at creation" do
|
44
|
+
subject { ContextIO::ConnectToken.new(api, resource_url: 'http://example.com/hitme') }
|
45
|
+
|
46
|
+
it "loads it from the API" do
|
47
|
+
api.should_receive(:request).with(
|
48
|
+
:get,
|
49
|
+
'http://example.com/hitme'
|
50
|
+
).and_return({
|
51
|
+
'token' => 'baphoo'
|
52
|
+
})
|
53
|
+
|
54
|
+
expect(subject.token).to eq('baphoo')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'contextio/email_settings'
|
3
|
+
|
4
|
+
describe ContextIO::EmailSettings do
|
5
|
+
let(:api) { double('API') }
|
6
|
+
|
7
|
+
subject { ContextIO::EmailSettings.new(api, 'email@email.com') }
|
8
|
+
|
9
|
+
describe ".new" do
|
10
|
+
context "without a source type passed in" do
|
11
|
+
it "takes an api handle" do
|
12
|
+
expect(subject.api).to eq(api)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "takes an email address" do
|
16
|
+
expect(subject.email).to eq('email@email.com')
|
17
|
+
end
|
18
|
+
|
19
|
+
it "defaults the source type to 'IMAP'" do
|
20
|
+
expect(subject.source_type).to eq('IMAP')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "with a source type passed in" do
|
25
|
+
subject { ContextIO::EmailSettings.new(api, 'email@email.com', 'FOO') }
|
26
|
+
|
27
|
+
it "takes a source type argument" do
|
28
|
+
expect(subject.source_type).to eq('FOO')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#resource_url" do
|
34
|
+
it "builds the right url" do
|
35
|
+
expect(subject.resource_url).to eq('discovery')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#documentation" do
|
40
|
+
it "fetches it from the api" do
|
41
|
+
api.should_receive(:request).with(:get, anything, anything).and_return({ 'documentation' => ['foo', 'bar'] })
|
42
|
+
expect(subject.documentation).to eq(['foo', 'bar'])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#found?" do
|
47
|
+
it "fetches it from the api" do
|
48
|
+
api.should_receive(:request).with(:get, anything, anything).and_return({ 'found' => true })
|
49
|
+
expect(subject).to be_found
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#type" do
|
54
|
+
it "fetches it from the api" do
|
55
|
+
api.should_receive(:request).with(:get, anything, anything).and_return({ 'type' => 'gmail' })
|
56
|
+
expect(subject.type).to eq('gmail')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#server" do
|
61
|
+
it "fetches it from the api" do
|
62
|
+
api.should_receive(:request).with(:get, anything, anything).and_return({ 'imap' => { 'server' => 'imap.gmail.com' }})
|
63
|
+
expect(subject.server).to eq('imap.gmail.com')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#username" do
|
68
|
+
it "fetches it from the api" do
|
69
|
+
api.should_receive(:request).with(:get, anything, anything).and_return({ 'imap' => { 'username' => 'ben' }})
|
70
|
+
expect(subject.username).to eq('ben')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#port" do
|
75
|
+
it "fetches it from the api" do
|
76
|
+
api.should_receive(:request).with(:get, anything, anything).and_return({ 'imap' => { 'port' => 993 }})
|
77
|
+
expect(subject.port).to eq(993)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#oauth?" do
|
82
|
+
it "fetches it from the api" do
|
83
|
+
api.should_receive(:request).with(:get, anything, anything).and_return({ 'imap' => { 'oauth' => false }})
|
84
|
+
expect(subject).to_not be_oauth
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "#use_ssl?" do
|
89
|
+
it "fetches it from the api" do
|
90
|
+
api.should_receive(:request).with(:get, anything, anything).and_return({ 'imap' => { 'use_ssl' => false }})
|
91
|
+
expect(subject).to_not be_use_ssl
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#fetch_attributes" do
|
96
|
+
before do
|
97
|
+
api.stub(:request).with(:get, anything, anything).and_return({ 'foo' => 'bar' })
|
98
|
+
end
|
99
|
+
|
100
|
+
it "defines a getter if one doesn't already exist" do
|
101
|
+
subject.send(:fetch_attributes)
|
102
|
+
|
103
|
+
expect(subject.foo).to eq('bar')
|
104
|
+
end
|
105
|
+
|
106
|
+
it "hits the right URL" do
|
107
|
+
api.should_receive(:request).with(:get, 'discovery', 'email' => 'email@email.com', 'source_type' => 'IMAP')
|
108
|
+
|
109
|
+
subject.send(:fetch_attributes)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|