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,43 @@
|
|
1
|
+
require 'contextio/api/resource'
|
2
|
+
|
3
|
+
class ContextIO
|
4
|
+
class Contact
|
5
|
+
include ContextIO::API::Resource
|
6
|
+
|
7
|
+
self.primary_key = :email
|
8
|
+
self.association_name = :contact
|
9
|
+
|
10
|
+
belongs_to :account
|
11
|
+
|
12
|
+
lazy_attributes :emails, :name, :thumbnail, :last_received, :last_sent,
|
13
|
+
:count
|
14
|
+
private :last_received, :last_sent
|
15
|
+
|
16
|
+
def email
|
17
|
+
@email ||= emails.first
|
18
|
+
end
|
19
|
+
|
20
|
+
def last_received_at
|
21
|
+
last_received ? Time.at(last_received) : nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def last_sent_at
|
25
|
+
last_sent ? Time.at(last_sent) : nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# Poor man's has_many
|
29
|
+
def threads
|
30
|
+
account.threads.where(email: email)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Poor man's has_many
|
34
|
+
def messages
|
35
|
+
account.messages.where(email: email)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Poor man's has_many
|
39
|
+
def files
|
40
|
+
account.files.where(email: email)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'api/resource_collection'
|
2
|
+
require_relative 'contact'
|
3
|
+
|
4
|
+
class ContextIO
|
5
|
+
class ContactCollection
|
6
|
+
include ContextIO::API::ResourceCollection
|
7
|
+
|
8
|
+
self.resource_class = ContextIO::Contact
|
9
|
+
self.association_name = :contacts
|
10
|
+
|
11
|
+
belongs_to :account
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# The request that comes back is not formatted the same as other
|
16
|
+
# collections, so we need to override it and pick out the right key.
|
17
|
+
def attribute_hashes
|
18
|
+
@attribute_hashes ||= api.request(:get, resource_url, where_constraints)['matches']
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'contextio/api/association_helpers'
|
2
|
+
|
3
|
+
class ContextIO
|
4
|
+
class EmailAddress
|
5
|
+
def self.association_name
|
6
|
+
:email_address
|
7
|
+
end
|
8
|
+
ContextIO::API::AssociationHelpers.register_resource(self, :email_address)
|
9
|
+
|
10
|
+
# (see ContextIO#api)
|
11
|
+
attr_reader :api, :account, :email, :validated, :primary
|
12
|
+
private :validated, :primary
|
13
|
+
#
|
14
|
+
# @private
|
15
|
+
#
|
16
|
+
# For internal use only. Users of this gem shouldn't be calling this
|
17
|
+
# directly.
|
18
|
+
#
|
19
|
+
# @param [API] api A handle on the Context.IO API.
|
20
|
+
# @param [Hash{String, Symbol => String, Numeric, Boolean}] options A Hash
|
21
|
+
# of attributes describing the resource.
|
22
|
+
def initialize(api, options = {})
|
23
|
+
@api = api
|
24
|
+
@account = options.delete(:account) || options.delete('account')
|
25
|
+
|
26
|
+
options.each do |key, value|
|
27
|
+
instance_variable_set("@#{key}", value)
|
28
|
+
|
29
|
+
unless self.respond_to?(key)
|
30
|
+
define_singleton_method(key) do
|
31
|
+
instance_variable_get("@#{key}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def validated?
|
38
|
+
!!validated
|
39
|
+
end
|
40
|
+
|
41
|
+
def primary?
|
42
|
+
!!primary
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_primary
|
46
|
+
api.request(:post, resource_url, primary: 1)['success']
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete
|
50
|
+
api.request(:delete, resource_url)['success']
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'api/resource_collection'
|
2
|
+
require_relative 'email_address'
|
3
|
+
|
4
|
+
class ContextIO
|
5
|
+
class EmailAddressCollection
|
6
|
+
include ContextIO::API::ResourceCollection
|
7
|
+
|
8
|
+
self.resource_class = ContextIO::EmailAddress
|
9
|
+
self.association_name = :email_addresses
|
10
|
+
|
11
|
+
belongs_to :account
|
12
|
+
|
13
|
+
def create(address)
|
14
|
+
result_hash = api.request(:post, resource_url, email_address: address)
|
15
|
+
|
16
|
+
result_hash.delete('success')
|
17
|
+
|
18
|
+
resource_class.new(api, result_hash)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
class ContextIO
|
2
|
+
# Represents the IMAP settings for a given email address. The API
|
3
|
+
# documentation refers to this as 'discovery'. This process is hit-and-miss.
|
4
|
+
# If noting is found, many attributes are likely to be nil and blank. Check
|
5
|
+
# {EmailSettings#found? EmailSettings#found?}.
|
6
|
+
class EmailSettings
|
7
|
+
# (see ContextIO#api)
|
8
|
+
attr_reader :api
|
9
|
+
|
10
|
+
# @!attribute [r] type
|
11
|
+
# @return [String] The type og the provider (like 'gmail'). Will fetch from
|
12
|
+
# the API if necessary.
|
13
|
+
def type
|
14
|
+
return @type if instance_variable_defined?(:@type)
|
15
|
+
|
16
|
+
fetch_attributes
|
17
|
+
|
18
|
+
@type
|
19
|
+
end
|
20
|
+
|
21
|
+
# @!attribute [r] documentation
|
22
|
+
# @return [Array<String>] A list of documentation pages that pertain to the
|
23
|
+
# provider. Will fetch from the API if necessary.
|
24
|
+
def documentation
|
25
|
+
return @documentation if instance_variable_defined?(:@documentation)
|
26
|
+
|
27
|
+
fetch_attributes
|
28
|
+
|
29
|
+
@documentation
|
30
|
+
end
|
31
|
+
|
32
|
+
# @!attribute [r] email
|
33
|
+
# @return [String] The email address associated with these IMAP settings.
|
34
|
+
attr_reader :email, :source_type
|
35
|
+
|
36
|
+
# Not sure why, but the below comment has to be below the attr_reader call
|
37
|
+
# above.
|
38
|
+
|
39
|
+
# @!attribute [r] source_type
|
40
|
+
# @return [String] The only source type currently supported by the API is
|
41
|
+
# 'IMAP'.
|
42
|
+
|
43
|
+
# (see ContextIO::OAuthProviderCollection#initialize)
|
44
|
+
# @param [String] email The email address to fetch the settings for.
|
45
|
+
# @param [String] source_type The only source type currently supported by
|
46
|
+
# the API is 'IMAP'.
|
47
|
+
def initialize(api, email, source_type = 'IMAP')
|
48
|
+
@api = api
|
49
|
+
@email = email
|
50
|
+
@source_type = source_type
|
51
|
+
end
|
52
|
+
|
53
|
+
# @!attribute [r] resource_url
|
54
|
+
# @return [String] The path for discovering email settings.
|
55
|
+
def resource_url
|
56
|
+
'discovery'
|
57
|
+
end
|
58
|
+
|
59
|
+
# @!attribute [r] found?
|
60
|
+
# @return [Boolean] Whether the settings were able to be fetched. Will fetch
|
61
|
+
# from the API if necessary.
|
62
|
+
def found?
|
63
|
+
found
|
64
|
+
end
|
65
|
+
|
66
|
+
# @!attribute [r] server
|
67
|
+
# @return [String] FQDN of the IMAP server. Will fetch from the API if
|
68
|
+
# necessary.
|
69
|
+
def server
|
70
|
+
imap['server']
|
71
|
+
end
|
72
|
+
|
73
|
+
# @!attribute [r] username
|
74
|
+
# @return [String] The username for authentication purposes. Will fetch
|
75
|
+
# from the API if necessary.
|
76
|
+
def username
|
77
|
+
imap['username']
|
78
|
+
end
|
79
|
+
|
80
|
+
# @!attribute [r] port
|
81
|
+
# @return [Integer] The network port the IMAP server is listening on. Will
|
82
|
+
# fetch from the API if necessary.
|
83
|
+
def port
|
84
|
+
imap['port']
|
85
|
+
end
|
86
|
+
|
87
|
+
# @!attribute [r] oauth?
|
88
|
+
# @return [Boolean] Whether the IMAP server supports OAuth or not. Will
|
89
|
+
# fetch from the API if necessary.
|
90
|
+
def oauth?
|
91
|
+
!!imap['oauth']
|
92
|
+
end
|
93
|
+
|
94
|
+
# @!attribute [r] use_ssl?
|
95
|
+
# @return [Boolean] Whether the IMAP server uses SSL for connections. Will
|
96
|
+
# fetch from the API if necessary.
|
97
|
+
def use_ssl?
|
98
|
+
!!imap['use_ssl']
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
# @!attribute [r] imap
|
104
|
+
# @return [Hash{String => String, Boolean, Integer}] Attributes of the IMAP
|
105
|
+
# server in question.
|
106
|
+
def imap
|
107
|
+
return @imap if instance_variable_defined?(:@imap)
|
108
|
+
|
109
|
+
fetch_attributes
|
110
|
+
|
111
|
+
@imap
|
112
|
+
end
|
113
|
+
|
114
|
+
# @!attribute [r] found
|
115
|
+
# @return [Boolean] Whether the settings were able to be fetched. Will fetch
|
116
|
+
# from the API if necessary.
|
117
|
+
def found
|
118
|
+
return @found if instance_variable_defined?(:@found)
|
119
|
+
|
120
|
+
fetch_attributes
|
121
|
+
|
122
|
+
@found
|
123
|
+
end
|
124
|
+
|
125
|
+
# Fetches attributes from the API for the email settings.
|
126
|
+
#
|
127
|
+
# Defines getter methods for any attributes that come back and don't
|
128
|
+
# already have them. This way, if the API expands, the gem will still let
|
129
|
+
# users get attributes we didn't explicitly declare as lazy.
|
130
|
+
def fetch_attributes
|
131
|
+
attr_hashes = api.request(:get, resource_url, 'email' => email, 'source_type' => source_type)
|
132
|
+
|
133
|
+
attr_hashes.each do |key, value|
|
134
|
+
instance_variable_set("@#{key}", value)
|
135
|
+
|
136
|
+
unless respond_to?(key)
|
137
|
+
instance_eval <<-RUBY
|
138
|
+
def #{key}
|
139
|
+
@#{key}
|
140
|
+
end
|
141
|
+
RUBY
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'contextio/api/resource'
|
2
|
+
require 'contextio/source_sync_data'
|
3
|
+
|
4
|
+
class ContextIO
|
5
|
+
class File
|
6
|
+
include ContextIO::API::Resource
|
7
|
+
|
8
|
+
self.primary_key = :file_id
|
9
|
+
self.association_name = :file
|
10
|
+
|
11
|
+
belongs_to :account
|
12
|
+
|
13
|
+
lazy_attributes :size, :type, :subject, :date, :addresses, :personInfo,
|
14
|
+
:email_message_id, :message_id, :date_indexed,
|
15
|
+
:date_received, :file_name, :file_name_structure,
|
16
|
+
:body_section, :content_disposition, :file_id,
|
17
|
+
:is_tnef_part, :gmail_message_id, :gmail_thread_id,
|
18
|
+
:supports_preview, :is_embedded
|
19
|
+
private :date, :addresses, :message_id, :date_indexed, :date_received,
|
20
|
+
:is_tnef_part, :is_embedded, :personInfo
|
21
|
+
|
22
|
+
def received_at
|
23
|
+
@received_at ||= Time.at(date_received)
|
24
|
+
end
|
25
|
+
|
26
|
+
def indexed_at
|
27
|
+
@indexed_at ||= Time.at(indexed_at)
|
28
|
+
end
|
29
|
+
|
30
|
+
def to
|
31
|
+
addresses['to']
|
32
|
+
end
|
33
|
+
|
34
|
+
def from
|
35
|
+
addresses['from']
|
36
|
+
end
|
37
|
+
|
38
|
+
def person_info
|
39
|
+
personInfo
|
40
|
+
end
|
41
|
+
|
42
|
+
def tnef_part?
|
43
|
+
!!is_tnef_part
|
44
|
+
end
|
45
|
+
|
46
|
+
def embedded?
|
47
|
+
!!is_embedded
|
48
|
+
end
|
49
|
+
|
50
|
+
def content
|
51
|
+
@content ||= api.request(:get, "#{resource_url}/content")
|
52
|
+
end
|
53
|
+
|
54
|
+
def content_link
|
55
|
+
@content_link ||= api.raw_request(:get, "#{resource_url}/content", as_link: 1)
|
56
|
+
end
|
57
|
+
|
58
|
+
def related_files
|
59
|
+
return @related_files if @related_files
|
60
|
+
|
61
|
+
attribute_hashes = api.request(:get, "#{resource_url}/related")
|
62
|
+
|
63
|
+
@related_files = FileCollection.new(api, attribute_hashes: attribute_hashes, account: account)
|
64
|
+
|
65
|
+
return @related_files
|
66
|
+
end
|
67
|
+
|
68
|
+
def revisions
|
69
|
+
return @revisions if @revisions
|
70
|
+
|
71
|
+
attribute_hashes = api.request(:get, "#{resource_url}/revisions")
|
72
|
+
|
73
|
+
@revisions = FileCollection.new(api, attribute_hashes: attribute_hashes, account: account)
|
74
|
+
|
75
|
+
return @revisions
|
76
|
+
end
|
77
|
+
|
78
|
+
def sync_data
|
79
|
+
return @sync_data if @sync_data
|
80
|
+
|
81
|
+
sync_hashes = api.request(:get, "#{resource_url}/sync")
|
82
|
+
|
83
|
+
@sync_data = ContextIO::SourceSyncData.new(sync_hashes)
|
84
|
+
|
85
|
+
return @sync_data
|
86
|
+
end
|
87
|
+
|
88
|
+
def sync!
|
89
|
+
api.request(:post, "#{resource_url}/sync")['success']
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'api/resource_collection'
|
2
|
+
require_relative 'file'
|
3
|
+
|
4
|
+
class ContextIO
|
5
|
+
class FileCollection
|
6
|
+
include ContextIO::API::ResourceCollection
|
7
|
+
|
8
|
+
self.resource_class = ContextIO::File
|
9
|
+
self.association_name = :files
|
10
|
+
|
11
|
+
belongs_to :account
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'contextio/api/association_helpers'
|
2
|
+
|
3
|
+
class ContextIO
|
4
|
+
class Folder
|
5
|
+
def self.association_name
|
6
|
+
:folder
|
7
|
+
end
|
8
|
+
ContextIO::API::AssociationHelpers.register_resource(self, :folder)
|
9
|
+
|
10
|
+
# (see ContextIO#api)
|
11
|
+
attr_reader :api, :source, :name, :attributes, :delim, :nb_messages,
|
12
|
+
:uidvalidity, :nb_unseen_messages
|
13
|
+
private :attributes
|
14
|
+
|
15
|
+
# @private
|
16
|
+
#
|
17
|
+
# For internal use only. Users of this gem shouldn't be calling this
|
18
|
+
# directly.
|
19
|
+
#
|
20
|
+
# @param [API] api A handle on the Context.IO API.
|
21
|
+
# @param [Hash{String, Symbol => String, Numeric, Boolean}] options A Hash
|
22
|
+
# of attributes describing the resource.
|
23
|
+
def initialize(api, options = {})
|
24
|
+
@api = api
|
25
|
+
@source = options.delete(:source) || options.delete('source')
|
26
|
+
|
27
|
+
options.each do |key, value|
|
28
|
+
instance_variable_set("@#{key}", value)
|
29
|
+
|
30
|
+
unless self.respond_to?(key)
|
31
|
+
define_singleton_method(key) do
|
32
|
+
instance_variable_get("@#{key}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def has_children?
|
39
|
+
attributes['HasChildren']
|
40
|
+
end
|
41
|
+
|
42
|
+
def marked?
|
43
|
+
attributes['Marked']
|
44
|
+
end
|
45
|
+
|
46
|
+
def imap_attributes
|
47
|
+
attributes
|
48
|
+
end
|
49
|
+
|
50
|
+
def messages
|
51
|
+
association_class = ContextIO::API::AssociationHelpers.class_for_association_name(:messages)
|
52
|
+
|
53
|
+
@messages ||= association_class.new(api, folder: self)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|