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,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module ContextIO
|
4
|
+
# Methods related to authentication and configuration.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
module Authentication
|
8
|
+
# @return [Hash] The authentication details for OAuth.
|
9
|
+
def authentication
|
10
|
+
{
|
11
|
+
:consumer_key => consumer_key,
|
12
|
+
:consumer_secret => consumer_secret,
|
13
|
+
:token => nil,
|
14
|
+
:token_secret => nil,
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Boolean] Whether authentication details are configured or not.
|
19
|
+
def authenticated?
|
20
|
+
authentication[:consumer_key] and authentication[:consumer_secret]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'context-io/version'
|
2
|
+
|
3
|
+
module ContextIO
|
4
|
+
# Defines constants and methods related to configuration
|
5
|
+
#
|
6
|
+
# You shouldn't interact with this module directly, as it's included in the
|
7
|
+
# {ContextIO} module. Mostly you'll use the {#configure} method on
|
8
|
+
# {ContextIO}, see the example on {#configure}.
|
9
|
+
#
|
10
|
+
# @see #configure
|
11
|
+
module Config
|
12
|
+
# The HTTP connection adapter that will be used to connect if none is set
|
13
|
+
DEFAULT_ADAPTER = :net_http
|
14
|
+
|
15
|
+
# The Faraday connection options if none is set
|
16
|
+
DEFAULT_CONNECTION_OPTIONS = {}
|
17
|
+
|
18
|
+
# The consumer key if none is set
|
19
|
+
DEFAULT_CONSUMER_KEY = nil
|
20
|
+
|
21
|
+
# The consumer secret if none is set
|
22
|
+
DEFAULT_CONSUMER_SECRET = nil
|
23
|
+
|
24
|
+
# The proxy server if none is set
|
25
|
+
DEFAULT_PROXY = nil
|
26
|
+
|
27
|
+
# The value of sent in the 'User-Agent' header if none is set
|
28
|
+
DEFAULT_USER_AGENT = "context-io ruby gem #{ContextIO::VERSION}"
|
29
|
+
|
30
|
+
# The configuration options that are settable.
|
31
|
+
VALID_OPTIONS_KEYS = [
|
32
|
+
:adapter,
|
33
|
+
:connection_options,
|
34
|
+
:consumer_key,
|
35
|
+
:consumer_secret,
|
36
|
+
:proxy,
|
37
|
+
:user_agent
|
38
|
+
]
|
39
|
+
|
40
|
+
# @return [Symbol] The HTTP connection adapter. Check the Faraday
|
41
|
+
# documentation for possible adapters.
|
42
|
+
attr_accessor :adapter
|
43
|
+
|
44
|
+
# @return [Hash] Connection options passed to the Faraday connection object.
|
45
|
+
attr_accessor :connection_options
|
46
|
+
|
47
|
+
# @return [String] The OAuth consumer key received from Context.IO.
|
48
|
+
attr_accessor :consumer_key
|
49
|
+
|
50
|
+
# @return [String] The OAuth consumer secret received from Context.IO.
|
51
|
+
attr_accessor :consumer_secret
|
52
|
+
|
53
|
+
# @return [String, URI] The URI to the HTTP proxy to use.
|
54
|
+
attr_accessor :proxy
|
55
|
+
|
56
|
+
# @return [String] The value to be sent in the User-Agent header. Probably
|
57
|
+
# doesn't need to be changed unless you're writing a big app, or another
|
58
|
+
# library based on this one.
|
59
|
+
attr_accessor :user_agent
|
60
|
+
|
61
|
+
# Makes sure the default values are always set
|
62
|
+
#
|
63
|
+
# @api private
|
64
|
+
def self.extended(base)
|
65
|
+
base.reset
|
66
|
+
end
|
67
|
+
|
68
|
+
# Configure with a block
|
69
|
+
#
|
70
|
+
# @api public
|
71
|
+
#
|
72
|
+
# @example Configuring OAuth
|
73
|
+
# ContextIO.configure do |config|
|
74
|
+
# config.consumer_key = 'my_consumer_key'
|
75
|
+
# config.consumer_secret = 'my_consumer_secret'
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# @return [self]
|
79
|
+
def configure
|
80
|
+
yield self
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
# Reset the configuration to the default values
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
#
|
88
|
+
# @example Reset the configuration
|
89
|
+
# ContextIO.adapter = :some_erroneous_thing
|
90
|
+
# ContextIO.reset
|
91
|
+
# ContextIO.adapter # => DEFAULT_ADAPTER
|
92
|
+
#
|
93
|
+
# @return [void]
|
94
|
+
def reset
|
95
|
+
self.adapter = DEFAULT_ADAPTER
|
96
|
+
self.connection_options = DEFAULT_CONNECTION_OPTIONS
|
97
|
+
self.consumer_key = DEFAULT_CONSUMER_KEY
|
98
|
+
self.consumer_secret = DEFAULT_CONSUMER_SECRET
|
99
|
+
self.proxy = DEFAULT_PROXY
|
100
|
+
self.user_agent = DEFAULT_USER_AGENT
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'context-io/core_ext/hash'
|
3
|
+
require 'context-io/request/oauth'
|
4
|
+
require 'context-io/response/parse_json'
|
5
|
+
require 'context-io/response/raise_client_error'
|
6
|
+
require 'context-io/response/raise_server_error'
|
7
|
+
|
8
|
+
module ContextIO
|
9
|
+
# Methods for creating a connection to the API server.
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
# @private
|
13
|
+
module Connection
|
14
|
+
# Create and configure a Faraday connection
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
#
|
18
|
+
# @param [true, false] raw Set this to true to disable JSON parsing of the
|
19
|
+
# response body.
|
20
|
+
#
|
21
|
+
# @return [Faraday::Connection] A Connection object that's correctly
|
22
|
+
# configured.
|
23
|
+
def connection(raw=false)
|
24
|
+
default_options = {
|
25
|
+
:headers => {
|
26
|
+
:accept => 'application/json',
|
27
|
+
:user_agent => ContextIO.user_agent,
|
28
|
+
},
|
29
|
+
:proxy => ContextIO.proxy,
|
30
|
+
:ssl => { :verify => false },
|
31
|
+
:url => 'https://api.context.io'
|
32
|
+
}
|
33
|
+
Faraday.new(default_options.deep_merge(ContextIO.connection_options)) do |builder|
|
34
|
+
builder.use ContextIO::Request::ContextIOOAuth, ContextIO.authentication if ContextIO.authenticated?
|
35
|
+
builder.use Faraday::Request::UrlEncoded
|
36
|
+
builder.use ContextIO::Response::RaiseClientError
|
37
|
+
builder.use ContextIO::Response::ParseJson unless raw
|
38
|
+
builder.use ContextIO::Response::RaiseServerError
|
39
|
+
builder.adapter(ContextIO.adapter)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
private :connection
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Hash extensions
|
2
|
+
#
|
3
|
+
# This is borrowed from ActiveSupport. We don't want the entire ActiveSupport
|
4
|
+
# library (it's huge), so we'll just add this method.
|
5
|
+
class Hash
|
6
|
+
# Merge self with another hash recursively
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
#
|
10
|
+
# @param [Hash] hash The hash to merge into this one.
|
11
|
+
#
|
12
|
+
# @example Merge two hashes with some common keys
|
13
|
+
# a_hash = { :foo => :bar, :baz => { :foobar => "hey" }}
|
14
|
+
# another_hash = { :foo => :foobar, :baz => { :foo => :bar }}
|
15
|
+
# a_hash.deep_merge(another_hash)
|
16
|
+
# # => { :foo => :foobar, :baz => { :foobar => "hey", :foo => :bar }}
|
17
|
+
#
|
18
|
+
# @return [Hash] The given hash merged recursively into this one.
|
19
|
+
def deep_merge(hash)
|
20
|
+
target = self.dup
|
21
|
+
hash.keys.each do |key|
|
22
|
+
if hash[key].is_a?(Hash) && self[key].is_a?(Hash)
|
23
|
+
target[key] = target[key].deep_merge(hash[key])
|
24
|
+
next
|
25
|
+
end
|
26
|
+
target[key] = hash[key]
|
27
|
+
end
|
28
|
+
target
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module ContextIO
|
4
|
+
# Base class for ContextIO exceptions.
|
5
|
+
#
|
6
|
+
# @api public
|
7
|
+
class Error < StandardError
|
8
|
+
# @return [Hash{String => String}] The HTTP headers of the response.
|
9
|
+
attr_reader :http_headers
|
10
|
+
|
11
|
+
# Initialize a new ContextIO error
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
# @private
|
15
|
+
#
|
16
|
+
# @param [String] message The error message.
|
17
|
+
# @param [Hash{String => String}] http_headers The HTTP headers.
|
18
|
+
def initialize(message, http_headers)
|
19
|
+
@http_headers = Hash[http_headers]
|
20
|
+
super(message)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'context-io/error/client_error'
|
2
|
+
|
3
|
+
module ContextIO
|
4
|
+
# Raised when Context.IO returns the HTTP status code 400
|
5
|
+
#
|
6
|
+
# This usually means that some required info is missing.
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class Error::BadRequest < ContextIO::Error::ClientError
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'context-io/error/client_error'
|
2
|
+
|
3
|
+
module ContextIO
|
4
|
+
# Raised when ContextIO returns the HTTP status code 403
|
5
|
+
#
|
6
|
+
# This usually means that the resource isn't accessible.
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class Error::Forbidden < ContextIO::Error::ClientError
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'context-io/error/client_error'
|
2
|
+
|
3
|
+
module ContextIO
|
4
|
+
# Raised when Context.IO returns the HTTP status code 404
|
5
|
+
#
|
6
|
+
# This means that the resource you tried to get doesn't exist.
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class Error::NotFound < ContextIO::Error::ClientError
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'context-io/error/client_error'
|
2
|
+
|
3
|
+
module ContextIO
|
4
|
+
# Raised when Context.IO returns the HTTP status code 402
|
5
|
+
#
|
6
|
+
# This means that you're trying to do something that your plan isn't allowed
|
7
|
+
# to do (anymore), so you need to log in and upgrade your plan.
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class Error::PaymentRequired < ContextIO::Error::ClientError
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'context-io/error/server_error'
|
2
|
+
|
3
|
+
module ContextIO
|
4
|
+
# Raised when Context.IO returns the HTTP status code 503
|
5
|
+
#
|
6
|
+
# The 503 status code means a request required a connection to the mail
|
7
|
+
# server, but that connection failed.
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class Error::ServiceUnavailable < ContextIO::Error::ServerError
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'context-io/error/client_error'
|
2
|
+
|
3
|
+
module ContextIO
|
4
|
+
# Raised when Context.IO returns the HTTP status code 401
|
5
|
+
#
|
6
|
+
# This means that the OAuth signature can't be validated.
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class Error::Unauthorized < ContextIO::Error::ClientError
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'context-io/resource'
|
2
|
+
|
3
|
+
module ContextIO
|
4
|
+
# A file found as an email attachment
|
5
|
+
#
|
6
|
+
# @api public
|
7
|
+
class File < Resource
|
8
|
+
# @api public
|
9
|
+
# @return [String] The ID of the file
|
10
|
+
attr_reader :id
|
11
|
+
|
12
|
+
# @api public
|
13
|
+
# @return [Integer] The size of the file, in bytes.
|
14
|
+
attr_reader :size
|
15
|
+
|
16
|
+
# @api public
|
17
|
+
# @return [String] The MIME type of the file.
|
18
|
+
attr_reader :type
|
19
|
+
|
20
|
+
# @api public
|
21
|
+
# @return [String] The subject of the message this file was attached to
|
22
|
+
attr_reader :subject
|
23
|
+
|
24
|
+
# @api public
|
25
|
+
# @return [Time] When this file was sent
|
26
|
+
attr_reader :date
|
27
|
+
|
28
|
+
# @api public
|
29
|
+
# @return [Hash] Information on the different addresses attached to this
|
30
|
+
# file's message.
|
31
|
+
attr_reader :addresses
|
32
|
+
|
33
|
+
# @api public
|
34
|
+
# @return [String] The full filename
|
35
|
+
attr_reader :file_name
|
36
|
+
|
37
|
+
# @api public
|
38
|
+
# @return [Integer]
|
39
|
+
attr_reader :body_section
|
40
|
+
|
41
|
+
# @api public
|
42
|
+
# @return [true, false] Whether the file supports preview
|
43
|
+
attr_reader :supports_preview
|
44
|
+
|
45
|
+
# @api public
|
46
|
+
# @return [String] The (Context.IO) ID of the message this file was attached to
|
47
|
+
attr_reader :message_id
|
48
|
+
|
49
|
+
# @api public
|
50
|
+
# @return [Time] When Context.IO indexed the file (not the same as when it
|
51
|
+
# was sent)
|
52
|
+
attr_reader :date_indexed
|
53
|
+
|
54
|
+
# @api public
|
55
|
+
# @return [String] The email message ID (The Message-ID header)
|
56
|
+
attr_reader :email_message_id
|
57
|
+
|
58
|
+
# @api public
|
59
|
+
# @return [Hash] Information about the people involved with the message
|
60
|
+
attr_reader :person_info
|
61
|
+
|
62
|
+
# @api public
|
63
|
+
# @return [Array] The file name split up into parts
|
64
|
+
attr_reader :file_name_structure
|
65
|
+
|
66
|
+
# Get all files for a given account, optionally filtered with a query
|
67
|
+
#
|
68
|
+
# @see Account#messages
|
69
|
+
#
|
70
|
+
# @api public
|
71
|
+
#
|
72
|
+
# @param [Account, #to_s] account The account or account ID to search for
|
73
|
+
# files in.
|
74
|
+
# @param [Hash] query A query to filter files by.
|
75
|
+
# @option query [String, Regexp] :file_name The filename to search for. The
|
76
|
+
# string can contain shell globs ('*', '?' and '[]').
|
77
|
+
# @option query [String] :email The email address of the contact for whom
|
78
|
+
# you want the latest files exchanged with. By "exchanged with contact X",
|
79
|
+
# we mean any email received from contact X, sent to contact X or sent by
|
80
|
+
# anyone to both contact X and the source owner.
|
81
|
+
# @option query [String] :to The email address of a contact files have been
|
82
|
+
# sent to.
|
83
|
+
# @option query [String] :from The email address of a contact files have
|
84
|
+
# been sent from.
|
85
|
+
# @option query [String] :cc The email address of a contact CC'ed on the
|
86
|
+
# messages.
|
87
|
+
# @option query [String] :bcc The email address of a contact BCC'ed on the
|
88
|
+
# messages.
|
89
|
+
# @option query [#to_i] :date_before Only include files attached to messages
|
90
|
+
# sent before this timestamp. The value of this filter is applied to the
|
91
|
+
# Date header of the message, which refers to the time the message is sent
|
92
|
+
# from the origin.
|
93
|
+
# @option query [#to_i] :date_after Only include files attached to messages
|
94
|
+
# sent after this timestamp. The value of this filter is applied to the
|
95
|
+
# Date header of the message, which refers to the time the message is sent
|
96
|
+
# from the origin.
|
97
|
+
# @option query [#to_i] :indexed_before Only include files attached to
|
98
|
+
# messages indexed before this timestamp. This is not the same as the date
|
99
|
+
# of the email, it is the time Context.IO indexed this message.
|
100
|
+
# @option query [#to_i] :indexed_after Only include files attached to
|
101
|
+
# messages indexed after this timestamp. This is not the same as the date
|
102
|
+
# of the email, it is the time Context.IO indexed this message.
|
103
|
+
# @option query [true, false] :group_by_revisions (false) If this is set to
|
104
|
+
# true, the method will return an array of Hashes, where each Hash
|
105
|
+
# represents a group of revisions of the same file. The Hash has an
|
106
|
+
# `:occurences` field, which is an Array of {File} objects, a `:file_name`
|
107
|
+
# field, which is the name of the file, and a `:latest_date` field, which
|
108
|
+
# is a Time object representing the last time a message with this file was
|
109
|
+
# sent.
|
110
|
+
# @option query [#to_i] :limit The maximum count of results to return.
|
111
|
+
# @option query [#to_i] :offset (0) The offset to begin returning files at.
|
112
|
+
#
|
113
|
+
# @example Get all files for the account
|
114
|
+
# files = ContextIO::File.all(account)
|
115
|
+
#
|
116
|
+
# @example Get 10 files that we have sent
|
117
|
+
# files = ContextIO::File.all(account,
|
118
|
+
# :from => account.email_addresses.first,
|
119
|
+
# :limit => 10
|
120
|
+
# )
|
121
|
+
#
|
122
|
+
# @example Find PDF files
|
123
|
+
# files = ContextIO::File.all(account, :file_name => '*.pdf')
|
124
|
+
#
|
125
|
+
# @example Find JP(E)G files
|
126
|
+
# files = ContextIO::File.all(account, :file_name => /\.jpe?g$/)
|
127
|
+
#
|
128
|
+
# @return [Array<File>, Array<Hash>] The matching file objects. If the
|
129
|
+
# `:group_by_revisions` flag is set, the return value changes, see the
|
130
|
+
# documentation for that flag above.
|
131
|
+
def self.all(account, query={})
|
132
|
+
if query[:file_name] && query[:file_name].is_a?(Regexp)
|
133
|
+
query[:file_name] = "/#{query[:file_name].source}/"
|
134
|
+
end
|
135
|
+
|
136
|
+
[:date_before, :date_after, :indexed, :indexed_after].each do |field|
|
137
|
+
if query[field] && query[field].respond_to?(:to_i)
|
138
|
+
query[field] = query[field].to_i
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
if query[:group_by_revisions]
|
143
|
+
query[:group_by_revisions] = query[:group_by_revisions] ? '1' : '0'
|
144
|
+
end
|
145
|
+
|
146
|
+
account_id = account.is_a?(Account) ? account.id : account.to_s
|
147
|
+
get("/2.0/accounts/#{account_id}/files", query).map do |file|
|
148
|
+
if query[:group_by_revisions]
|
149
|
+
occurences = file['occurences'].map do |file|
|
150
|
+
File.from_json(account_id, file)
|
151
|
+
end
|
152
|
+
|
153
|
+
{
|
154
|
+
:occurences => occurences,
|
155
|
+
:file_name => file['file_name'],
|
156
|
+
:latest_date => Time.at(file['latest_date'].to_i)
|
157
|
+
}
|
158
|
+
else
|
159
|
+
File.from_json(account_id, file)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Fetch the content of the message.
|
165
|
+
#
|
166
|
+
# @api public
|
167
|
+
#
|
168
|
+
# @note Data transfer for this call is metered and charged at the end of the
|
169
|
+
# month. See [Context.IO's pricing page](http://context.io/pricing) for
|
170
|
+
# more info.
|
171
|
+
#
|
172
|
+
# @return [String] The raw contents of the file.
|
173
|
+
def content
|
174
|
+
get("/2.0/accounts/#@account_id/files/#@id/content", :raw => true)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Create a File with the JSON from Context.IO.
|
178
|
+
#
|
179
|
+
# @api private
|
180
|
+
#
|
181
|
+
# @param [String] account_id The account ID.
|
182
|
+
# @param [Hash] json The parsed JSON object returned by a Context.IO API
|
183
|
+
# request. See their documentation for possible keys.
|
184
|
+
#
|
185
|
+
# @return [File] A file with the given attributes.
|
186
|
+
def self.from_json(account_id, json)
|
187
|
+
file = new
|
188
|
+
file.instance_eval do
|
189
|
+
@id = json['file_id']
|
190
|
+
@account_id = account_id
|
191
|
+
@size = json['size']
|
192
|
+
@type = json['type']
|
193
|
+
@subject = json['subject']
|
194
|
+
@date = Time.at(json['date'])
|
195
|
+
@addresses = {}
|
196
|
+
json['addresses'].each do |type, info|
|
197
|
+
@addresses[type.to_sym] = {}
|
198
|
+
if info.is_a?(Hash)
|
199
|
+
info.each do |key, value|
|
200
|
+
@addresses[type.to_sym][key.to_sym] = value
|
201
|
+
end
|
202
|
+
elsif info.is_a?(Array)
|
203
|
+
@addresses[type.to_sym] = []
|
204
|
+
info.each do |email_info|
|
205
|
+
@addresses[type.to_sym] << {}
|
206
|
+
email_info.each do |key, value|
|
207
|
+
@addresses[type.to_sym].last[key.to_sym] = value
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
@file_name = json['file_name']
|
213
|
+
@body_section = json['body_section']
|
214
|
+
@supports_preview = json['supports_preview']
|
215
|
+
@message_id = json['message_id']
|
216
|
+
@date_indexed = Time.at(json['date_indexed'])
|
217
|
+
@email_message_id = json['email_message_id']
|
218
|
+
@person_info = {}
|
219
|
+
json['person_info'].each do |email, info|
|
220
|
+
@person_info[email] = {}
|
221
|
+
info.each do |key, value|
|
222
|
+
@person_info[email][key.to_sym] = value
|
223
|
+
end
|
224
|
+
end
|
225
|
+
@file_name_structure = []
|
226
|
+
json['file_name_structure'].each do |part|
|
227
|
+
@file_name_structure << [part.first, part.last.to_sym]
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
file
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|