contextio 0.5.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,18 @@
|
|
1
|
+
require_relative 'api/resource_collection'
|
2
|
+
require_relative 'folder'
|
3
|
+
|
4
|
+
class ContextIO
|
5
|
+
class FolderCollection
|
6
|
+
include ContextIO::API::ResourceCollection
|
7
|
+
|
8
|
+
self.resource_class = ContextIO::Folder
|
9
|
+
self.association_name = :folders
|
10
|
+
|
11
|
+
belongs_to :source
|
12
|
+
|
13
|
+
def create(folder_name, folder_delimiter='/')
|
14
|
+
api.request(:put, "#{resource_url}/#{folder_name}", delim: folder_delimiter)['success']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class ContextIO
|
2
|
+
class FolderSyncData
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(name, attr_hash)
|
6
|
+
@name = name
|
7
|
+
@attr_hash = attr_hash
|
8
|
+
end
|
9
|
+
|
10
|
+
def initial_import_finished?
|
11
|
+
attr_hash['initial_import_finished']
|
12
|
+
end
|
13
|
+
|
14
|
+
def last_expunged_at
|
15
|
+
Time.at(attr_hash['last_expunge'])
|
16
|
+
end
|
17
|
+
|
18
|
+
def last_sync_started_at
|
19
|
+
Time.at(attr_hash['last_sync_start'])
|
20
|
+
end
|
21
|
+
|
22
|
+
def last_sync_stopped_at
|
23
|
+
Time.at(attr_hash['last_sync_stop'])
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def attr_hash
|
29
|
+
@attr_hash
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'contextio/api/resource'
|
2
|
+
|
3
|
+
class ContextIO
|
4
|
+
class Message
|
5
|
+
include ContextIO::API::Resource
|
6
|
+
|
7
|
+
self.primary_key = :message_id
|
8
|
+
self.association_name = :message
|
9
|
+
|
10
|
+
belongs_to :account
|
11
|
+
has_many :sources
|
12
|
+
has_many :body_parts
|
13
|
+
|
14
|
+
lazy_attributes :date, :folders, :addresses, :subject, :list_help,
|
15
|
+
:list_unsubscribe, :message_id, :email_message_id,
|
16
|
+
:gmail_message_id, :gmail_thread_id, :person_info,
|
17
|
+
:date_received, :date_indexed
|
18
|
+
private :date_received, :date_indexed
|
19
|
+
|
20
|
+
def received_at
|
21
|
+
@received_at ||= Time.at(date_received)
|
22
|
+
end
|
23
|
+
|
24
|
+
def indexed_at
|
25
|
+
@indexed_at ||= Time.at(date_indexed)
|
26
|
+
end
|
27
|
+
|
28
|
+
def flags
|
29
|
+
api.request(:get, "#{resource_url}/flags")
|
30
|
+
end
|
31
|
+
|
32
|
+
# As of this writing, the documented valid flags are: seen, answered,
|
33
|
+
# flagged, deleted, and draft. However, this will send whatever you send it.
|
34
|
+
def set_flags(flag_hash)
|
35
|
+
args = flag_hash.map({}) do |memo, (flag_name, value)|
|
36
|
+
memo[flag_name] = value ? 1 : 0
|
37
|
+
memo
|
38
|
+
end
|
39
|
+
|
40
|
+
api.request(:post, resource_url, args)['success']
|
41
|
+
end
|
42
|
+
|
43
|
+
def folders
|
44
|
+
api.request(:get, "#{resource_url}/folders").collect { |f| f['name'] }
|
45
|
+
end
|
46
|
+
|
47
|
+
def headers
|
48
|
+
api.request(:get, "#{resource_url}/headers")
|
49
|
+
end
|
50
|
+
|
51
|
+
def raw
|
52
|
+
api.raw_request(:get, "#{resource_url}/source")
|
53
|
+
end
|
54
|
+
|
55
|
+
# You can call this with a Folder object, in which case, the source from the
|
56
|
+
# folder will be used, or you can pass in a folder name and source label.
|
57
|
+
def copy_to(folder, source = nil)
|
58
|
+
if folder.is_a?(ContextIO::Folder)
|
59
|
+
folder_name = folder.name
|
60
|
+
source_label = folder.source.label
|
61
|
+
else
|
62
|
+
folder_name = folder.to_s
|
63
|
+
source_label = source.to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
api.request(:post, resource_url, dst_folder: folder_name, dst_source: source_label)['success']
|
67
|
+
end
|
68
|
+
|
69
|
+
# You can call this with a Folder object, in which case, the source from the
|
70
|
+
# folder will be used, or you can pass in a folder name and source label.
|
71
|
+
def move_to(folder, source = nil)
|
72
|
+
if folder.is_a?(ContextIO::Folder)
|
73
|
+
folder_name = folder.name
|
74
|
+
source_label = folder.source.label
|
75
|
+
else
|
76
|
+
folder_name = folder.to_s
|
77
|
+
source_label = source.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
api.request(:post, resource_url, dst_folder: folder_name, dst_source: source_label, move: 1)['success']
|
81
|
+
end
|
82
|
+
|
83
|
+
def delete
|
84
|
+
api.request(:delete, resource_url)['success']
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# The API doesn't return enough information to build a resource_url for this
|
90
|
+
# resource, or the resource_url its self, making this problematic. Marking
|
91
|
+
# this private until something is sorted on that front.
|
92
|
+
def thread
|
93
|
+
ContextIO::Thread.new(api, api.request(:get, "#{resource_url}/thread").merge(account: account))
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'api/resource_collection'
|
2
|
+
require_relative 'message'
|
3
|
+
|
4
|
+
class ContextIO
|
5
|
+
class MessageCollection
|
6
|
+
include ContextIO::API::ResourceCollection
|
7
|
+
|
8
|
+
self.resource_class = ContextIO::Message
|
9
|
+
self.association_name = :messages
|
10
|
+
|
11
|
+
belongs_to :account
|
12
|
+
|
13
|
+
# You can pass a Folder object and this'll use the source from it, or you
|
14
|
+
# can pass a folder label and a source label (from the API), if that's
|
15
|
+
# easier for you.
|
16
|
+
#
|
17
|
+
# This is private because AFAICT, the oauth gem doesn't do POST requests
|
18
|
+
# with Content-Type = 'multipart/form-data' easily. I think it might behoove
|
19
|
+
# us to replace that dependency in the future, anyway, and we can fix this
|
20
|
+
# at that point. In any case, this functionality was missing from previous
|
21
|
+
# releases of the contextio gem, too.
|
22
|
+
def create(raw_message, folder, source = nil)
|
23
|
+
if folder.is_a?(ContextIO::Folder)
|
24
|
+
folder_label = folder.name
|
25
|
+
source_label = source || folder.source.label
|
26
|
+
else
|
27
|
+
folder_label = folder.to_s
|
28
|
+
source_label = source.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
api.request(:post, resource_url, message: raw_message, dst_folder: folder_label, dst_source: source_label)['success']
|
32
|
+
end
|
33
|
+
private :create
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'contextio/api/resource'
|
2
|
+
|
3
|
+
class ContextIO
|
4
|
+
# Represents a single OAuth provider for an account. You can use this to
|
5
|
+
# inspect or delete the provider. Most of the attributes are lazily loaded,
|
6
|
+
# meaning that the API won't get hit until you ask for an attribute the object
|
7
|
+
# doesn't already have (presumably from a previous API call).
|
8
|
+
class OAuthProvider
|
9
|
+
include API::Resource
|
10
|
+
|
11
|
+
# @!attribute [r] provider_consumer_key
|
12
|
+
# @return [String] The consumer key associated with this provider. Will
|
13
|
+
# fetch from the API if necessary.
|
14
|
+
# @!attribute [r] provider_consumer_secret
|
15
|
+
# @return [String] The consumer secret associated with this provider. Will
|
16
|
+
# fetch from the API if necessary.
|
17
|
+
# @!attribute [r] type
|
18
|
+
# @return [String] The consumer key associated with this provider. Will
|
19
|
+
# fetch from the API if necessary.
|
20
|
+
lazy_attributes :provider_consumer_key, :provider_consumer_secret, :type
|
21
|
+
|
22
|
+
self.primary_key = :provider_consumer_key
|
23
|
+
self.association_name = :oauth_provider
|
24
|
+
|
25
|
+
def delete
|
26
|
+
api.request(:delete, resource_url)['success']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative 'oauth_provider'
|
2
|
+
require_relative 'api/resource_collection'
|
3
|
+
|
4
|
+
class ContextIO
|
5
|
+
# Represents a collection of OAuth providers for an account. You can use this
|
6
|
+
# to create a proider, fetch a specific one or iterate over them.
|
7
|
+
#
|
8
|
+
# @example You can iterate over them with `each`:
|
9
|
+
# contextio.oauth_providers.each do |oauth_provider|
|
10
|
+
# puts oauth_provider.type
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# @example You can lazily access a specific one with square brackets:
|
14
|
+
# provider = contextio.oauth_providers['some_provider_consumer_key']
|
15
|
+
class OAuthProviderCollection
|
16
|
+
include ContextIO::API::ResourceCollection
|
17
|
+
|
18
|
+
self.resource_class = ContextIO::OAuthProvider
|
19
|
+
self.association_name = :oauth_providers
|
20
|
+
|
21
|
+
# Creates a new OAuth provider for your account.
|
22
|
+
#
|
23
|
+
# @param [String] type The type of provider. As of this writing, the API
|
24
|
+
# only accepts 'GMAIL' and 'GOOGLEAPPSMARKETPLACE'.
|
25
|
+
# @param [String] provider_consumer_key The Provider Consumer Key you got
|
26
|
+
# when you OAuthed the user.
|
27
|
+
# @param [String] provider_consumer_secret The Provider Consumer Secret you
|
28
|
+
# got when you OAuthed the user.
|
29
|
+
#
|
30
|
+
# @return [OAuthProvider] A new provider instance based on the data you
|
31
|
+
# input.
|
32
|
+
def create(type, provider_consumer_key, provider_consumer_secret)
|
33
|
+
result_hash = api.request(
|
34
|
+
:post,
|
35
|
+
resource_url,
|
36
|
+
type: type,
|
37
|
+
provider_consumer_key: provider_consumer_key,
|
38
|
+
provider_consumer_secret: provider_consumer_secret
|
39
|
+
)
|
40
|
+
|
41
|
+
result_hash.delete('success')
|
42
|
+
|
43
|
+
resource_class.new(api, result_hash)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'contextio/api/resource'
|
2
|
+
|
3
|
+
class ContextIO
|
4
|
+
class Source
|
5
|
+
include ContextIO::API::Resource
|
6
|
+
|
7
|
+
self.primary_key = :label
|
8
|
+
self.association_name = :source
|
9
|
+
|
10
|
+
belongs_to :account
|
11
|
+
has_many :folders
|
12
|
+
|
13
|
+
lazy_attributes :server, :label, :username, :port, :authentication_type,
|
14
|
+
:status, :service_level, :sync_period, :use_ssl, :type
|
15
|
+
private :use_ssl
|
16
|
+
|
17
|
+
# @!attribute [r] use_ssl?
|
18
|
+
# @return [Boolean] Whether or not this source uses SSL.
|
19
|
+
def use_ssl?
|
20
|
+
use_ssl
|
21
|
+
end
|
22
|
+
|
23
|
+
# Updates the source.
|
24
|
+
#
|
25
|
+
# @params [Hash{String, Symbol => String}] options You can update the
|
26
|
+
# following: status, sync_period, service_level, password, provider_token,
|
27
|
+
# provider_token_secret, provider_consumer_key. See the Context.IO docs
|
28
|
+
# for more details on these fields.
|
29
|
+
def update(options={})
|
30
|
+
updatable_attrs = %w(status sync_period service_level password
|
31
|
+
provider_token provider_token_secret
|
32
|
+
provider_consumer_key)
|
33
|
+
|
34
|
+
options.keep_if do |key, value|
|
35
|
+
updatable_attrs.include?(key.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
return nil if options.empty?
|
39
|
+
|
40
|
+
it_worked = api.request(:post, resource_url, options)['success']
|
41
|
+
|
42
|
+
if it_worked
|
43
|
+
options.each do |key, value|
|
44
|
+
instance_variable_set("@#{key}", value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it_worked
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete
|
52
|
+
api.request(:delete, resource_url)['success']
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative 'api/resource_collection'
|
2
|
+
require_relative 'source'
|
3
|
+
|
4
|
+
class ContextIO
|
5
|
+
class SourceCollection
|
6
|
+
include ContextIO::API::ResourceCollection
|
7
|
+
|
8
|
+
self.resource_class = ContextIO::Source
|
9
|
+
self.association_name = :sources
|
10
|
+
|
11
|
+
belongs_to :account
|
12
|
+
|
13
|
+
# Creates a new source for an account.
|
14
|
+
#
|
15
|
+
# @param [String] email The email address for the new source.
|
16
|
+
# @param [String] server The address of the server for the source.
|
17
|
+
# @param [String] username The name for logging into the server. Often the
|
18
|
+
# same as the email.
|
19
|
+
# @param [Boolean] use_ssl Whether to use SSL for the new source.
|
20
|
+
# @param [Numeric, String] port The port to connect on.
|
21
|
+
# @param [String] type Currently, only 'IMAP' is supported.
|
22
|
+
# @param [Hash{String, Symbol => String}] options Information you can
|
23
|
+
# provide at creation. Check out the Context.IO documentation for what's
|
24
|
+
# required and what's optional.
|
25
|
+
def create(email, server, username, use_ssl, port, type, options={})
|
26
|
+
api_args = options.merge(
|
27
|
+
'email' => email,
|
28
|
+
'username' => username,
|
29
|
+
'use_ssl' => use_ssl ? '1' : '0',
|
30
|
+
'port' => port.to_s,
|
31
|
+
'type' => type
|
32
|
+
)
|
33
|
+
|
34
|
+
result_hash = api.request(:post, resource_url, api_args)
|
35
|
+
|
36
|
+
result_hash.delete('success')
|
37
|
+
|
38
|
+
resource_class.new(api, result_hash)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'folder_sync_data'
|
2
|
+
|
3
|
+
class ContextIO
|
4
|
+
class SourceSyncData
|
5
|
+
attr_reader :folder_names, :folders, :source_label
|
6
|
+
|
7
|
+
def initialize(source_label, folder_hash)
|
8
|
+
@folder_hash = folder_hash
|
9
|
+
@source_label = source_label
|
10
|
+
@folder_names = folder_hash.keys
|
11
|
+
|
12
|
+
@folders = folder_hash.collect do |folder_name, attr_hash|
|
13
|
+
ContextIO::FolderSyncData.new(folder_name, attr_hash)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def folder_hash
|
20
|
+
@folder_hash
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'contextio/api/resource'
|
2
|
+
|
3
|
+
class ContextIO
|
4
|
+
class Thread
|
5
|
+
include ContextIO::API::Resource
|
6
|
+
|
7
|
+
self.primary_key = :gmail_thread_id
|
8
|
+
self.association_name = :thread
|
9
|
+
|
10
|
+
belongs_to :account
|
11
|
+
has_many :messages
|
12
|
+
|
13
|
+
lazy_attributes :gmail_thread_id, :email_message_ids, :person_info
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'api/resource_collection'
|
2
|
+
require_relative 'thread'
|
3
|
+
|
4
|
+
class ContextIO
|
5
|
+
class ThreadCollection
|
6
|
+
include ContextIO::API::ResourceCollection
|
7
|
+
|
8
|
+
self.resource_class = ContextIO::Thread
|
9
|
+
self.association_name = :threads
|
10
|
+
|
11
|
+
belongs_to :account
|
12
|
+
|
13
|
+
# Iterates over the resources in question.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# contextio.connect_tokens.each do |connect_token|
|
17
|
+
# puts connect_token.email
|
18
|
+
# end
|
19
|
+
def each(&block)
|
20
|
+
attribute_hashes.each do |actually_a_resource_url|
|
21
|
+
yield resource_class.new(api, {resource_url: actually_a_resource_url}.merge(associations_hash))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'contextio/api/resource'
|
2
|
+
|
3
|
+
class ContextIO
|
4
|
+
class Webhook
|
5
|
+
include ContextIO::API::Resource
|
6
|
+
|
7
|
+
self.primary_key = :webhook_id
|
8
|
+
self.association_name = :webhook
|
9
|
+
|
10
|
+
belongs_to :account
|
11
|
+
|
12
|
+
lazy_attributes :callback_url, :failure_notif_url, :active, :sync_period,
|
13
|
+
:failure, :webhook_id, :filter_to, :filter_from, :filter_cc,
|
14
|
+
:filter_subject, :filter_thread, :filter_new_important,
|
15
|
+
:filter_file_name, :filter_file_revisions,
|
16
|
+
:filter_folder_added, :filter_folder_removed
|
17
|
+
private :active, :failure
|
18
|
+
|
19
|
+
def active?
|
20
|
+
!!active
|
21
|
+
end
|
22
|
+
|
23
|
+
def failure?
|
24
|
+
!!failure
|
25
|
+
end
|
26
|
+
|
27
|
+
def activate
|
28
|
+
api.request(:post, resource_url, active: 1)['success']
|
29
|
+
end
|
30
|
+
|
31
|
+
def deactivate
|
32
|
+
api.request(:post, resource_url, active: 0)['success']
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete
|
36
|
+
api.request(:delete, resource_url)['success']
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|