context-io 0.0.1
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/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +129 -0
- data/Rakefile +121 -0
- data/SPEC.md +49 -0
- data/context-io.gemspec +96 -0
- data/lib/context-io.rb +14 -0
- data/lib/context-io/account.rb +254 -0
- data/lib/context-io/authentication.rb +23 -0
- data/lib/context-io/config.rb +103 -0
- data/lib/context-io/connection.rb +45 -0
- data/lib/context-io/core_ext/hash.rb +31 -0
- data/lib/context-io/error.rb +24 -0
- data/lib/context-io/error/bad_request.rb +12 -0
- data/lib/context-io/error/client_error.rb +10 -0
- data/lib/context-io/error/forbidden.rb +12 -0
- data/lib/context-io/error/internal_server_error.rb +10 -0
- data/lib/context-io/error/not_found.rb +12 -0
- data/lib/context-io/error/payment_required.rb +13 -0
- data/lib/context-io/error/server_error.rb +10 -0
- data/lib/context-io/error/service_unavailable.rb +13 -0
- data/lib/context-io/error/unauthorized.rb +12 -0
- data/lib/context-io/file.rb +234 -0
- data/lib/context-io/folder.rb +90 -0
- data/lib/context-io/message.rb +160 -0
- data/lib/context-io/oauth_provider.rb +84 -0
- data/lib/context-io/request.rb +70 -0
- data/lib/context-io/request/oauth.rb +44 -0
- data/lib/context-io/resource.rb +16 -0
- data/lib/context-io/response.rb +5 -0
- data/lib/context-io/response/parse_json.rb +30 -0
- data/lib/context-io/response/raise_client_error.rb +59 -0
- data/lib/context-io/response/raise_server_error.rb +32 -0
- data/lib/context-io/source.rb +193 -0
- data/lib/context-io/version.rb +7 -0
- data/spec/account_spec.rb +247 -0
- data/spec/contextio_spec.rb +45 -0
- data/spec/file_spec.rb +101 -0
- data/spec/fixtures/accounts.json +21 -0
- data/spec/fixtures/files.json +41 -0
- data/spec/fixtures/files_group.json +47 -0
- data/spec/fixtures/folders.json +1 -0
- data/spec/fixtures/messages.json +1 -0
- data/spec/fixtures/oauth_providers.json +12 -0
- data/spec/fixtures/sources.json +1 -0
- data/spec/folder_spec.rb +48 -0
- data/spec/message_spec.rb +294 -0
- data/spec/oauth_provider_spec.rb +88 -0
- data/spec/source_spec.rb +248 -0
- data/spec/spec_helper.rb +4 -0
- metadata +214 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'context-io/connection'
|
2
|
+
require 'context-io/request'
|
3
|
+
|
4
|
+
module ContextIO
|
5
|
+
# The superclass of all resources provided by the API
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class Resource
|
9
|
+
include ContextIO::Connection
|
10
|
+
include ContextIO::Request
|
11
|
+
|
12
|
+
extend ContextIO::Connection
|
13
|
+
extend ContextIO::Request
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
module ContextIO
|
5
|
+
module Response
|
6
|
+
# Faraday middleware for parsing JSON in the response body
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class ParseJson < Faraday::Response::Middleware
|
10
|
+
# Parse the response body into JSON
|
11
|
+
#
|
12
|
+
# @param [#to_s] The response body, containing JSON data.
|
13
|
+
#
|
14
|
+
# @return [Object] The parsed JSON data, probably an Array or a Hash.
|
15
|
+
def parse(body)
|
16
|
+
case body
|
17
|
+
when ''
|
18
|
+
nil
|
19
|
+
when 'true'
|
20
|
+
true
|
21
|
+
when 'false'
|
22
|
+
false
|
23
|
+
else
|
24
|
+
::MultiJson.decode(body)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'context-io/error/bad_request'
|
3
|
+
require 'context-io/error/forbidden'
|
4
|
+
require 'context-io/error/not_found'
|
5
|
+
require 'context-io/error/unauthorized'
|
6
|
+
require 'context-io/error/payment_required'
|
7
|
+
|
8
|
+
module ContextIO
|
9
|
+
module Response
|
10
|
+
# Faraday middleware for raising errors on 4xx HTTP status codes
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
class RaiseClientError < Faraday::Response::Middleware
|
14
|
+
# Raise an error if the response has a 4xx status code
|
15
|
+
#
|
16
|
+
# @raise [ContextIO::Error::ClientError] If the response has a 4xx status
|
17
|
+
# code.
|
18
|
+
# @raise [ContextIO::Error::BadRequest] If the response has a 400 status
|
19
|
+
# code.
|
20
|
+
# @raise [ContextIO::Error::Unauthorized] If the response has a 401 status
|
21
|
+
# code.
|
22
|
+
# @raise [ContextIO::Error::PaymentRequired] If the response has a 402
|
23
|
+
# status code.
|
24
|
+
# @raise [ContextIO::Error::Forbidden] If the response has a 403 status
|
25
|
+
# code.
|
26
|
+
# @raise [ContextIO::Error::NotFound] If the response has a 404 status
|
27
|
+
# code.
|
28
|
+
#
|
29
|
+
# @return [void]
|
30
|
+
def on_complete(env)
|
31
|
+
case env[:status].to_i
|
32
|
+
when 400
|
33
|
+
raise ContextIO::Error::BadRequest.new(error_body(env[:body]), env[:response_headers])
|
34
|
+
when 401
|
35
|
+
raise ContextIO::Error::Unauthorized.new(error_body(env[:body]), env[:response_headers])
|
36
|
+
when 402
|
37
|
+
raise ContextIO::Error::PaymentRequired.new(error_body(env[:body]), env[:response_headers])
|
38
|
+
when 403
|
39
|
+
raise ContextIO::Error::Forbidden.new(error_body(env[:body]), env[:response_headers])
|
40
|
+
when 404
|
41
|
+
raise ContextIO::Error::NotFound.new(error_body(env[:body]), env[:response_headers])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# @return [String] The error message if one is defines, or an empty
|
48
|
+
# string.
|
49
|
+
def error_body(body)
|
50
|
+
if body && body['type'] == 'error' && body['value']
|
51
|
+
body['value']
|
52
|
+
else
|
53
|
+
''
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'context-io/error/internal_server_error'
|
3
|
+
require 'context-io/error/service_unavailable'
|
4
|
+
|
5
|
+
module ContextIO
|
6
|
+
module Response
|
7
|
+
# Faraday middleware for raising errors on 5xx status codes
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class RaiseServerError < Faraday::Response::Middleware
|
11
|
+
# Raise an error if the response has a 5xx status code
|
12
|
+
#
|
13
|
+
# @raise [ContextIO::Error::ServerError] If the response has a 5xx status
|
14
|
+
# code.
|
15
|
+
# @raise [ContextIO::Error::InternalServerError] If the response has a 500
|
16
|
+
# status code.
|
17
|
+
# @raise [ContextIO::Error::ServiceUnavailable] If the response has a 503
|
18
|
+
# status code.
|
19
|
+
#
|
20
|
+
# @return [void]
|
21
|
+
def on_complete(env)
|
22
|
+
case env[:status].to_i
|
23
|
+
when 500
|
24
|
+
raise ContextIO::Error::InternalServerError.new('Something is technically wrong.', env[:response_headers])
|
25
|
+
when 503
|
26
|
+
raise ContextIO::Error::ServiceUnavailable.new('Could not connect to mail server.', env[:response_headers])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'context-io/resource'
|
2
|
+
|
3
|
+
module ContextIO
|
4
|
+
# A message source. Create one of these for each mailbox a user has
|
5
|
+
#
|
6
|
+
# @api public
|
7
|
+
class Source < ContextIO::Resource
|
8
|
+
attr_accessor :label, :authentication_type, :port, :service_level, :username,
|
9
|
+
:server, :source_type, :sync_period, :use_ssl, :status
|
10
|
+
attr_reader :account_id, :email
|
11
|
+
|
12
|
+
# Public: Get all sources for given account.
|
13
|
+
#
|
14
|
+
# query - An optional Hash (default: {}) containing a query to filter the
|
15
|
+
# responses. For possible values see Context.IO API documentation.
|
16
|
+
# Returns an Array of Source objects.
|
17
|
+
#
|
18
|
+
# account - Account object or ID
|
19
|
+
def self.all(account, query = {})
|
20
|
+
return [] if account.nil?
|
21
|
+
|
22
|
+
account_id = account.is_a?(Account) ? account.id : account.to_s
|
23
|
+
get("/2.0/accounts/#{account_id}/sources", query).map do |msg|
|
24
|
+
Source.from_json(account_id, msg)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Find a source for given ID
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
#
|
32
|
+
# @param [String] label The label of the source to look up.
|
33
|
+
#
|
34
|
+
# @example Find the source with the labe 'foobar'
|
35
|
+
# ContextIO::Source.find('abcdef012345', 'foobar')
|
36
|
+
#
|
37
|
+
# @return [Source] The source with the given label.
|
38
|
+
def self.find(account, label)
|
39
|
+
return nil if account.nil? or label.to_s.empty?
|
40
|
+
account_id = account.is_a?(Account) ? account.id : account.to_s
|
41
|
+
|
42
|
+
Source.from_json(account_id, get("/2.0/accounts/#{account_id}/sources/#{label.to_s}"))
|
43
|
+
end
|
44
|
+
|
45
|
+
# Create a Source instance from the JSON returned by the Context.IO server
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
#
|
49
|
+
# @param [Hash] The parsed JSON object returned by a Context.IO API request.
|
50
|
+
# See their documentation for what keys are possible.
|
51
|
+
#
|
52
|
+
# @return [Source] A source with the given attributes.
|
53
|
+
def self.from_json(account_id, json)
|
54
|
+
source = new(account_id, json)
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(account_id, attributes = {})
|
58
|
+
raise ArgumentError if account_id.to_s.empty?
|
59
|
+
|
60
|
+
@account_id = account_id.to_s
|
61
|
+
@email = attributes['email']
|
62
|
+
@label = attributes['label'] || ''
|
63
|
+
@authentication_type = attributes['authentication_type']
|
64
|
+
@port = attributes['port'] || 143
|
65
|
+
@service_level = attributes['service_level']
|
66
|
+
@username = attributes['username']
|
67
|
+
@server = attributes['server']
|
68
|
+
@source_type = attributes['type'] || 'IMAP'
|
69
|
+
@sync_period = attributes['sync_period']
|
70
|
+
@use_ssl = attributes['use_ssl'] || false
|
71
|
+
@status = attributes['status']
|
72
|
+
@password = attributes['password']
|
73
|
+
@provider_token = attributes['provider_token']
|
74
|
+
@provider_token_secret = attributes['provider_token_secret']
|
75
|
+
@provider_consumer_key = attributes['provider_consumer_key']
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns all source's folders.
|
79
|
+
def folders
|
80
|
+
ContextIO::Folder.all(@account_id, @label)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Sends the source data to Context.IO
|
84
|
+
#
|
85
|
+
# If the source has been sent to Context.IO before, this will update
|
86
|
+
# allowed source attributes.
|
87
|
+
#
|
88
|
+
# @api public
|
89
|
+
#
|
90
|
+
# @raise [ArgumentError] If required arguments are missing.
|
91
|
+
#
|
92
|
+
# @example Create a source
|
93
|
+
# source = ContextIO::Source.new(@account.id, {'email' => 'me@example.com', 'server' => 'imap@example.com',
|
94
|
+
# 'username' => "me", 'use_ssl' => true, 'port' => 143, 'type' => 'IMAP'})
|
95
|
+
# source.save
|
96
|
+
#
|
97
|
+
# @return [true, false] Whether the save succeeded or not.
|
98
|
+
def save
|
99
|
+
@label.to_s.empty? ? create_record : update_record
|
100
|
+
end
|
101
|
+
|
102
|
+
# Destroys current source object
|
103
|
+
def destroy
|
104
|
+
return false if @label.to_s.empty?
|
105
|
+
|
106
|
+
response = delete("/2.0/accounts/#{@account_id}/sources/#{@label}")
|
107
|
+
@label = '' if response['success']
|
108
|
+
|
109
|
+
response['success']
|
110
|
+
end
|
111
|
+
|
112
|
+
# Update attributes on the Source object and then send them to Context.IO
|
113
|
+
#
|
114
|
+
# @api public
|
115
|
+
#
|
116
|
+
# @param [Hash] attributes The attributes to update. Allowed
|
117
|
+
# attributes are status, sync period, service level, password,
|
118
|
+
# provider token, provider token secret and provider consumer key
|
119
|
+
#
|
120
|
+
# @example Update the Source sync period to one day
|
121
|
+
# source.update_attributes('sync_period' => '1d')
|
122
|
+
#
|
123
|
+
# @return [true, false] Whether the update succeeded or not.
|
124
|
+
def update_attributes(attributes = {})
|
125
|
+
raise ArgumentError.new("Cannot set attributes on new record") if @label.to_s.empty?
|
126
|
+
|
127
|
+
attributes.each do |k,v|
|
128
|
+
if ["status", "sync_period", "service_level", "password", "provider_token", "provider_token_secret", "provider_consumer_key"].include? k
|
129
|
+
send("#{k}=", v)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
update_record
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
# Create the Source on Context.IO
|
138
|
+
#
|
139
|
+
# @api private
|
140
|
+
#
|
141
|
+
# @return [true, false] Whether the creation succeeded or not.
|
142
|
+
def create_record
|
143
|
+
if @email.to_s.empty? or @server.to_s.empty? or @username.to_s.empty? or @port.to_s.empty?
|
144
|
+
raise ArgumentError.new("Mandatory arguments are not set")
|
145
|
+
end
|
146
|
+
|
147
|
+
params = {}
|
148
|
+
params['email'] = @email.to_s
|
149
|
+
params['server'] = @server.to_s
|
150
|
+
params['username'] = @username.to_s
|
151
|
+
params['use_ssl'] = @use_ssl.to_s
|
152
|
+
params['port'] = @port.to_s
|
153
|
+
params['type'] = @source_type.to_s
|
154
|
+
|
155
|
+
# Optional parameters
|
156
|
+
params['service_level'] = @service_level if @service_level
|
157
|
+
params['sync_period'] = @sync_period if @sync_period
|
158
|
+
params['password'] = @password if @password
|
159
|
+
params['provider_token'] = @provider_token if @provider_token
|
160
|
+
params['provider_token_secret'] = @provider_token_secret if @provider_token_secret
|
161
|
+
params['provider_consumer_key'] = @provider_consumer_key if @provider_consumer_key
|
162
|
+
|
163
|
+
response = post("/2.0/accounts/#{@account_id}/sources", params)
|
164
|
+
response['success']
|
165
|
+
end
|
166
|
+
|
167
|
+
# Update existing Source on Context.IO
|
168
|
+
#
|
169
|
+
# Only sends the status, sync period, service level, password,
|
170
|
+
# provider token, provider token secret and provider consumer key
|
171
|
+
# as they are the only attributes the Context.IO API allows to be updated.
|
172
|
+
#
|
173
|
+
# @api private
|
174
|
+
#
|
175
|
+
# @return [true, false] Whether the update succeeded or not.
|
176
|
+
def update_record
|
177
|
+
return false if @label.to_s.empty?
|
178
|
+
|
179
|
+
atts = {}
|
180
|
+
|
181
|
+
atts["status"] = @status if @status
|
182
|
+
atts["sync_period"] = @sync_period if @sync_period
|
183
|
+
atts["service_level"] = @service_level if @service_level
|
184
|
+
atts["password"] = @password if @password
|
185
|
+
atts["provider_token"] = @provider_token if @provider_token
|
186
|
+
atts["provider_token_secret"] = @provider_token_secret if @provider_token_secret
|
187
|
+
atts["provider_consumer_key"] = @provider_consumer_key if @provider_consumer_key
|
188
|
+
|
189
|
+
response = post("/2.0/accounts/#{@account_id}/sources/#{@label}", atts)
|
190
|
+
response['success']
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ContextIO::Account do
|
4
|
+
let(:existing_account) do
|
5
|
+
stub_request(:post, 'https://api.context.io/2.0/accounts').
|
6
|
+
to_return(
|
7
|
+
:body => '{
|
8
|
+
"success": true,
|
9
|
+
"id": "1234567890abcdef",
|
10
|
+
"resource_url": "https://api.context.io/2.0/accounts/1234567890abcdef"
|
11
|
+
}')
|
12
|
+
account = ContextIO::Account.new(:email => 'foo@bar.com')
|
13
|
+
account.save
|
14
|
+
|
15
|
+
account
|
16
|
+
end
|
17
|
+
describe '.all' do
|
18
|
+
before(:each) do
|
19
|
+
json_accs = File.read(File.expand_path(File.join(File.dirname(__FILE__), "fixtures", "accounts.json")))
|
20
|
+
@stub = stub_request(:get, 'https://api.context.io/2.0/accounts').to_return(:body => json_accs)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns an array of Account objects' do
|
24
|
+
accounts = ContextIO::Account.all
|
25
|
+
accounts.first.should be_a(ContextIO::Account)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'calls the API request' do
|
29
|
+
ContextIO::Account.all
|
30
|
+
|
31
|
+
@stub.should have_been_requested
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'sets the attributes on the Account objects' do
|
35
|
+
ContextIO::Account.all.first.id.should == 'abcdef0123456789'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'sends a query if one is given' do
|
39
|
+
@stub = @stub.with(:query => {
|
40
|
+
:email => 'me@example.com',
|
41
|
+
:status => 'OK',
|
42
|
+
:status_ok => '1',
|
43
|
+
:limit => '1',
|
44
|
+
:offset => '0'
|
45
|
+
})
|
46
|
+
|
47
|
+
ContextIO::Account.all(
|
48
|
+
:email => 'me@example.com',
|
49
|
+
:status => :ok,
|
50
|
+
:status_ok => true,
|
51
|
+
:limit => 1,
|
52
|
+
:offset => 0
|
53
|
+
)
|
54
|
+
|
55
|
+
@stub.should have_been_requested
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '.find' do
|
60
|
+
before(:each) do
|
61
|
+
@stub = stub_request(:get, 'https://api.context.io/2.0/accounts/abcdef0123456789').to_return(
|
62
|
+
:body => '{
|
63
|
+
"id": "abcdef0123456789",
|
64
|
+
"username": "me.example.com_1234567890abcdef",
|
65
|
+
"created": 1234567890,
|
66
|
+
"suspended": 0,
|
67
|
+
"email_addresses": [ "me@example.com" ],
|
68
|
+
"first_name": "John",
|
69
|
+
"last_name": "Doe",
|
70
|
+
"password_expired": 0,
|
71
|
+
"sources": [{
|
72
|
+
"server": "mail.example.com",
|
73
|
+
"label": "me::mail.example.com",
|
74
|
+
"username": "me",
|
75
|
+
"port": 993,
|
76
|
+
"authentication_type": "password",
|
77
|
+
"use_ssl": true,
|
78
|
+
"sync_period": "1d",
|
79
|
+
"status": "OK",
|
80
|
+
"service_level": "pro",
|
81
|
+
"type": "imap"
|
82
|
+
}]
|
83
|
+
}'
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'returns an instance of Account' do
|
88
|
+
ContextIO::Account.find('abcdef0123456789').should be_a(ContextIO::Account)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'calls the API request' do
|
92
|
+
ContextIO::Account.find('abcdef0123456789')
|
93
|
+
|
94
|
+
@stub.should have_been_requested
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'sets the attributes on the Account object' do
|
98
|
+
ContextIO::Account.find('abcdef0123456789').id.should == 'abcdef0123456789'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '.new' do
|
103
|
+
it 'returns an instance of Account' do
|
104
|
+
ContextIO::Account.new.should be_a(ContextIO::Account)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'sets the given attributes on the Account object' do
|
108
|
+
account = ContextIO::Account.new(:first_name => 'John')
|
109
|
+
account.first_name.should == 'John'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#save' do
|
114
|
+
|
115
|
+
|
116
|
+
it 'returns true if the save was successful' do
|
117
|
+
@stub = stub_request(:post, 'https://api.context.io/2.0/accounts').
|
118
|
+
with(:body => { :email => 'me@example.com' }).
|
119
|
+
to_return(
|
120
|
+
:body => '{
|
121
|
+
"success": true,
|
122
|
+
"id": "abcdef0123456789",
|
123
|
+
"resource_url": "https://api.context.io/2.0/accounts/abcdef0123456789"
|
124
|
+
}'
|
125
|
+
)
|
126
|
+
|
127
|
+
account = ContextIO::Account.new(:email => 'me@example.com')
|
128
|
+
|
129
|
+
account.save.should be_true
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'returns false if the save was unsuccessful' do
|
133
|
+
@stub = stub_request(:post, 'https://api.context.io/2.0/accounts').
|
134
|
+
with(:body => { :email => 'me@example.com' }).
|
135
|
+
to_return(
|
136
|
+
:body => '{
|
137
|
+
"success": false
|
138
|
+
}'
|
139
|
+
)
|
140
|
+
|
141
|
+
account = ContextIO::Account.new(:email => 'me@example.com')
|
142
|
+
|
143
|
+
account.save.should be_false
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'for a new account' do
|
147
|
+
before(:each) do
|
148
|
+
@stub = stub_request(:post, 'https://api.context.io/2.0/accounts').
|
149
|
+
with(:body => { :email => 'me@example.com' }).
|
150
|
+
to_return(
|
151
|
+
:body => '{
|
152
|
+
"success": true,
|
153
|
+
"id": "abcdef0123456789",
|
154
|
+
"resource_url": "https://api.context.io/2.0/accounts/abcdef0123456789"
|
155
|
+
}'
|
156
|
+
)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'calls the API request' do
|
160
|
+
ContextIO::Account.new(:email => 'me@example.com').save
|
161
|
+
|
162
|
+
@stub.should have_been_requested
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'sets the ID of the account' do
|
166
|
+
account = ContextIO::Account.new(:email => 'me@example.com')
|
167
|
+
account.save
|
168
|
+
account.id.should == 'abcdef0123456789'
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'for an existing account' do
|
173
|
+
it 'calls the API request' do
|
174
|
+
@stub = stub_request(:put,
|
175
|
+
'https://api.context.io/2.0/accounts/1234567890abcdef').
|
176
|
+
with(:query => { :first_name => 'John' }).
|
177
|
+
to_return(
|
178
|
+
:body => '{
|
179
|
+
"success": true
|
180
|
+
}'
|
181
|
+
)
|
182
|
+
|
183
|
+
existing_account.first_name = 'John'
|
184
|
+
existing_account.save
|
185
|
+
|
186
|
+
@stub.should have_been_requested
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe '#update_attributes' do
|
192
|
+
it 'calls the API request' do
|
193
|
+
@stub = stub_request(:put,
|
194
|
+
'https://api.context.io/2.0/accounts/1234567890abcdef').
|
195
|
+
with(:query => { :first_name => 'John' }).
|
196
|
+
to_return(
|
197
|
+
:body => '{
|
198
|
+
"success": true
|
199
|
+
}'
|
200
|
+
)
|
201
|
+
|
202
|
+
existing_account.update_attributes(:first_name => 'John')
|
203
|
+
|
204
|
+
@stub.should have_been_requested
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'returns true if the update was successful' do
|
208
|
+
stub_request(:put, 'https://api.context.io/2.0/accounts/1234567890abcdef').
|
209
|
+
with(:query => { :first_name => 'John' }).
|
210
|
+
to_return(
|
211
|
+
:body => '{
|
212
|
+
"success": true
|
213
|
+
}'
|
214
|
+
)
|
215
|
+
|
216
|
+
existing_account.update_attributes(:first_name => 'John').should be_true
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'returns false if the update was unsuccessful' do
|
220
|
+
stub_request(:put, 'https://api.context.io/2.0/accounts/1234567890abcdef').
|
221
|
+
with(:query => { :first_name => 'John' }).
|
222
|
+
to_return(
|
223
|
+
:body => '{
|
224
|
+
"success": false
|
225
|
+
}'
|
226
|
+
)
|
227
|
+
|
228
|
+
existing_account.update_attributes(:first_name => 'John').should be_false
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
it 'sets the attributes on the account object' do
|
233
|
+
stub_request(:put, 'https://api.context.io/2.0/accounts/1234567890abcdef').
|
234
|
+
with(:query => { :first_name => 'John' }).
|
235
|
+
to_return(
|
236
|
+
:body => '{
|
237
|
+
"success": true
|
238
|
+
}'
|
239
|
+
)
|
240
|
+
|
241
|
+
existing_account.update_attributes(:first_name => 'John')
|
242
|
+
|
243
|
+
existing_account.first_name.should == 'John'
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|