context-io 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|