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,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
|