nylas 4.6.7 → 5.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6dd52077bca505902f8fe016a103318a1c426f74f01235a36c27a2463478f101
4
- data.tar.gz: 91f2addf66e656561881e721402955080f6e040536c3168737f7b62a4b74a9dd
3
+ metadata.gz: 8ab5c418509c447497fbd6106eb6e01fb7c23af451bf8150584b9bcf4e1ce736
4
+ data.tar.gz: 6827492459317c848f6f89c748a1cf5f5858506b83a578f53dbb4169010779ad
5
5
  SHA512:
6
- metadata.gz: ed82717d82cdc4b5fed03a09e7ee1553ba16071e4603b8cedce2518871fc44e2b34c8bd0ce5d9e077bb492a1d216de577264da104014c75e9964b44f7a238e52
7
- data.tar.gz: be58aff204cad1ce196f8fbeb766708d407d135a8247c484292a5a16fd821119727d21ded457b2b80650b58fc9c70d2a07c38dfc4beea94dc143454bc9377136
6
+ metadata.gz: 593d58ed82147ee51b00f90ed0e619eb4a6680b07cc8bae04eeba0809026e2e24ecce2565292c026bd2d164fba8a6a76f67768bf362c35c8e50bc0efbbe61da1
7
+ data.tar.gz: 10ed94be5d4e2c785ef92d32495301857f6c193e85e5087e96e8c2826b379219833f6db07c669c38fb7be573f77376748a6b323c1af46952c10853bf50e04233
data/lib/nylas.rb CHANGED
@@ -69,11 +69,25 @@ require_relative "nylas/deltas"
69
69
  require_relative "nylas/delta"
70
70
  require_relative "nylas/draft"
71
71
  require_relative "nylas/message"
72
+ require_relative "nylas/room_resource"
72
73
  require_relative "nylas/new_message"
73
74
  require_relative "nylas/raw_message"
74
75
  require_relative "nylas/thread"
75
76
  require_relative "nylas/webhook"
76
77
 
78
+ # Neural specific types
79
+ require_relative "nylas/neural"
80
+ require_relative "nylas/neural_sentiment_analysis"
81
+ require_relative "nylas/neural_ocr"
82
+ require_relative "nylas/neural_categorizer"
83
+ require_relative "nylas/neural_clean_conversation"
84
+ require_relative "nylas/neural_contact_link"
85
+ require_relative "nylas/neural_contact_name"
86
+ require_relative "nylas/neural_signature_contact"
87
+ require_relative "nylas/neural_signature_extraction"
88
+ require_relative "nylas/neural_message_options"
89
+ require_relative "nylas/categorize"
90
+
77
91
  require_relative "nylas/native_authentication"
78
92
 
79
93
  require_relative "nylas/http_client"
@@ -94,6 +108,7 @@ module Nylas
94
108
  Types.registry[:folder] = Types::ModelType.new(model: Folder)
95
109
  Types.registry[:im_address] = Types::ModelType.new(model: IMAddress)
96
110
  Types.registry[:label] = Types::ModelType.new(model: Label)
111
+ Types.registry[:room_resource] = Types::ModelType.new(model: RoomResource)
97
112
  Types.registry[:message] = Types::ModelType.new(model: Message)
98
113
  Types.registry[:message_headers] = MessageHeadersType.new
99
114
  Types.registry[:message_tracking] = Types::ModelType.new(model: MessageTracking)
@@ -108,4 +123,9 @@ module Nylas
108
123
  Types.registry[:contact_group] = Types::ModelType.new(model: ContactGroup)
109
124
  Types.registry[:when] = Types::ModelType.new(model: When)
110
125
  Types.registry[:time_slot] = Types::ModelType.new(model: TimeSlot)
126
+ Types.registry[:neural] = Types::ModelType.new(model: Neural)
127
+ Types.registry[:categorize] = Types::ModelType.new(model: Categorize)
128
+ Types.registry[:neural_signature_contact] = Types::ModelType.new(model: NeuralSignatureContact)
129
+ Types.registry[:neural_contact_link] = Types::ModelType.new(model: NeuralContactLink)
130
+ Types.registry[:neural_contact_name] = Types::ModelType.new(model: NeuralContactName)
111
131
  end
data/lib/nylas/api.rb CHANGED
@@ -6,7 +6,7 @@ module Nylas
6
6
  attr_accessor :client
7
7
 
8
8
  extend Forwardable
9
- def_delegators :client, :execute, :get, :post, :put, :delete, :app_id
9
+ def_delegators :client, :execute, :get, :post, :put, :delete, :app_id, :api_server
10
10
 
11
11
  include Logging
12
12
 
@@ -16,13 +16,11 @@ module Nylas
16
16
  # @param access_token [String] (Optional) Your users access token.
17
17
  # @param api_server [String] (Optional) Which Nylas API Server to connect to. Only change this if
18
18
  # you're using a self-hosted Nylas instance.
19
- # @param service_domain [String] (Optional) Host you are authenticating OAuth against.
20
19
  # @return [Nylas::API]
21
20
  def initialize(client: nil, app_id: nil, app_secret: nil, access_token: nil,
22
- api_server: "https://api.nylas.com", service_domain: "api.nylas.com")
21
+ api_server: "https://api.nylas.com")
23
22
  self.client = client || HttpClient.new(app_id: app_id, app_secret: app_secret,
24
- access_token: access_token, api_server: api_server,
25
- service_domain: service_domain)
23
+ access_token: access_token, api_server: api_server)
26
24
  end
27
25
 
28
26
  # @return [String] A Nylas access token for that particular user.
@@ -37,6 +35,19 @@ module Nylas
37
35
  )
38
36
  end
39
37
 
38
+ def authentication_url(redirect_uri:, scopes:, response_type: "code", login_hint: nil, state: nil)
39
+ params = {
40
+ client_id: app_id,
41
+ redirect_uri: redirect_uri,
42
+ response_type: response_type,
43
+ login_hint: login_hint
44
+ }
45
+ params[:state] = state if state
46
+ params[:scopes] = scopes.join(",") if scopes
47
+
48
+ "#{api_server}/oauth/authorize?#{URI.encode_www_form(params)}"
49
+ end
50
+
40
51
  def exchange_code_for_token(code)
41
52
  data = {
42
53
  "client_id" => app_id,
@@ -110,6 +121,16 @@ module Nylas
110
121
  @messages ||= Collection.new(model: Message, api: self)
111
122
  end
112
123
 
124
+ # @return[Collection<RoomResource>] A queryable collection of {RoomResource} objects
125
+ def room_resources
126
+ @room_resources ||= Collection.new(model: RoomResource, api: self)
127
+ end
128
+
129
+ # @return[Neural] A {Neural} object that provides
130
+ def neural
131
+ @neural ||= Neural.new(api: self)
132
+ end
133
+
113
134
  # Revokes access to the Nylas API for the given access token
114
135
  # @return [Boolean]
115
136
  def revoke(access_token)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Structure to represent the Neural Categorize object.
5
+ # @see https://developer.nylas.com/docs/intelligence/categorizer/#categorize-message-response
6
+ class Categorize
7
+ include Model::Attributable
8
+
9
+ attribute :category, :string
10
+ attribute :categorized_at, :unix_timestamp
11
+ attribute :model_version, :string
12
+ has_n_of_attribute :subcategories, :string
13
+ end
14
+ end
@@ -130,7 +130,7 @@ module Nylas
130
130
  response = api.execute(
131
131
  **to_be_executed.merge(
132
132
  path: "#{resources_path}/#{id}",
133
- query: {}
133
+ query: view_query
134
134
  )
135
135
  )
136
136
  model.from_hash(response, api: api)
@@ -147,5 +147,15 @@ module Nylas
147
147
  def execute
148
148
  api.execute(**to_be_executed)
149
149
  end
150
+
151
+ private
152
+
153
+ def view_query
154
+ if constraints.view
155
+ { view: constraints.view }
156
+ else
157
+ {}
158
+ end
159
+ end
150
160
  end
151
161
  end
data/lib/nylas/contact.rb CHANGED
@@ -13,9 +13,9 @@ module Nylas
13
13
  self.updatable = true
14
14
  self.destroyable = true
15
15
 
16
- attribute :id, :string, exclude_when: %i[creating updating]
16
+ attribute :id, :string, read_only: true
17
17
  attribute :object, :string, default: "contact"
18
- attribute :account_id, :string, exclude_when: %i[creating updating]
18
+ attribute :account_id, :string, read_only: true
19
19
 
20
20
  attribute :given_name, :string
21
21
  attribute :middle_name, :string
data/lib/nylas/delta.rb CHANGED
@@ -6,6 +6,7 @@ module Nylas
6
6
  # @see https://docs.nylas.com/reference#deltas
7
7
  class Delta
8
8
  include Model::Attributable
9
+
9
10
  attribute :id, :string
10
11
  attribute :type, :string
11
12
  attribute :object, :string
@@ -14,6 +15,8 @@ module Nylas
14
15
  attribute :namespace_id, :string
15
16
  attribute :account_id, :string
16
17
 
18
+ attribute :headers, :message_headers
19
+
17
20
  attribute :date, :unix_timestamp
18
21
  attribute :metadata, :hash
19
22
  attribute :object_attributes, :hash
@@ -24,6 +27,8 @@ module Nylas
24
27
  @model ||= Types.registry[object.to_sym].cast(object_attributes_with_ids)
25
28
  end
26
29
 
30
+ private
31
+
27
32
  def object_attributes_with_ids
28
33
  (object_attributes || {}).merge(id: id, account_id: account_id)
29
34
  end
@@ -42,8 +47,10 @@ module Nylas
42
47
  else
43
48
  data
44
49
  end
50
+
51
+ data = data.merge(data[:attributes]) if data[:attributes]
45
52
  data[:object_attributes] = data.delete(:attributes)
46
- super(data)
53
+ super(**data)
47
54
  end
48
55
  end
49
56
  end
data/lib/nylas/deltas.rb CHANGED
@@ -14,6 +14,6 @@ module Nylas
14
14
  attribute :cursor_end, :string
15
15
 
16
16
  extend Forwardable
17
- def_delegators :deltas, :count, :length, :each, :map, :first, :to_a, :empty?
17
+ def_delegators :deltas, :count, :length, :each, :map, :first, :last, :to_a, :empty?
18
18
  end
19
19
  end
data/lib/nylas/draft.rb CHANGED
@@ -29,7 +29,8 @@ module Nylas
29
29
  attribute :unread, :boolean
30
30
 
31
31
  has_n_of_attribute :events, :event
32
- has_n_of_attribute :files, :file
32
+ has_n_of_attribute :files, :file, read_only: true
33
+ has_n_of_attribute :file_ids, :string
33
34
  attribute :folder, :folder
34
35
  has_n_of_attribute :labels, :label
35
36
 
@@ -37,6 +38,20 @@ module Nylas
37
38
 
38
39
  transfer :api, to: %i[events files folder labels]
39
40
 
41
+ def update(**data)
42
+ self.files = data[:files] if data[:files]
43
+ extract_file_ids!
44
+ data[:file_ids] = file_ids
45
+
46
+ super
47
+ end
48
+
49
+ def create
50
+ extract_file_ids!
51
+
52
+ super
53
+ end
54
+
40
55
  def send!
41
56
  return execute(method: :post, path: "/send", payload: to_json) if tracking
42
57
 
@@ -53,7 +68,21 @@ module Nylas
53
68
  end
54
69
 
55
70
  def destroy
56
- execute(method: :delete, path: resource_path, payload: attributes.serialize(keys: [:version]))
71
+ execute(method: :delete, path: resource_path, payload: attributes.serialize_for_api(keys: [:version]))
72
+ end
73
+
74
+ private
75
+
76
+ def save_call
77
+ extract_file_ids!
78
+
79
+ super
80
+ end
81
+
82
+ def extract_file_ids!
83
+ files = self.files || []
84
+
85
+ self.file_ids = files.map(&:id)
57
86
  end
58
87
  end
59
88
  end
data/lib/nylas/event.rb CHANGED
@@ -9,9 +9,9 @@ module Nylas
9
9
  allows_operations(creatable: true, listable: true, filterable: true, showable: true, updatable: true,
10
10
  destroyable: true)
11
11
 
12
- attribute :id, :string
13
- attribute :object, :string
14
- attribute :account_id, :string
12
+ attribute :id, :string, read_only: true
13
+ attribute :object, :string, read_only: true
14
+ attribute :account_id, :string, read_only: true
15
15
  attribute :calendar_id, :string
16
16
  attribute :master_event_id, :string
17
17
  attribute :message_id, :string
@@ -27,6 +27,7 @@ module Nylas
27
27
  attribute :status, :string
28
28
  attribute :title, :string
29
29
  attribute :when, :when
30
+ attribute :metadata, :hash
30
31
  attribute :original_start_time, :unix_timestamp
31
32
 
32
33
  attr_accessor :notify_participants
@@ -5,6 +5,8 @@ module Nylas
5
5
 
6
6
  # Plain HTTP client that can be used to interact with the Nylas API sans any type casting.
7
7
  class HttpClient # rubocop:disable Metrics/ClassLength
8
+ HTTP_SUCCESS_CODES = [200, 201, 302].freeze
9
+
8
10
  HTTP_CODE_TO_EXCEPTIONS = {
9
11
  400 => InvalidRequest,
10
12
  401 => UnauthorizedRequest,
@@ -31,9 +33,10 @@ module Nylas
31
33
  "/delta/longpoll" => 3650,
32
34
  "/delta/streaming" => 3650
33
35
  }.freeze
36
+ SUPPORTED_API_VERSION = "2.2"
34
37
 
35
38
  include Logging
36
- attr_accessor :api_server, :service_domain
39
+ attr_accessor :api_server
37
40
  attr_writer :default_headers
38
41
  attr_reader :access_token
39
42
  attr_reader :app_id
@@ -44,10 +47,8 @@ module Nylas
44
47
  # @param access_token [String] (Optional) Your users access token.
45
48
  # @param api_server [String] (Optional) Which Nylas API Server to connect to. Only change this if
46
49
  # you're using a self-hosted Nylas instance.
47
- # @param service_domain [String] (Optional) Host you are authenticating OAuth against.
48
50
  # @return [Nylas::HttpClient]
49
- def initialize(app_id:, app_secret:, access_token: nil, api_server: "https://api.nylas.com",
50
- service_domain: "api.nylas.com")
51
+ def initialize(app_id:, app_secret:, access_token: nil, api_server: "https://api.nylas.com")
51
52
  unless api_server.include?("://")
52
53
  raise "When overriding the Nylas API server address, you must include https://"
53
54
  end
@@ -56,13 +57,12 @@ module Nylas
56
57
  @access_token = access_token
57
58
  @app_secret = app_secret
58
59
  @app_id = app_id
59
- @service_domain = service_domain
60
60
  end
61
61
 
62
62
  # @return [Nylas::HttpClient[]
63
63
  def as(access_token)
64
64
  HttpClient.new(app_id: app_id, access_token: access_token,
65
- app_secret: app_secret, api_server: api_server, service_domain: service_domain)
65
+ app_secret: app_secret, api_server: api_server)
66
66
  end
67
67
 
68
68
  # Sends a request to the Nylas API and rai
@@ -92,7 +92,12 @@ module Nylas
92
92
  content_type = response.headers[:content_type].downcase
93
93
  end
94
94
 
95
- response = parse_response(response) if content_type == "application/json"
95
+ begin
96
+ response = parse_response(response) if content_type == "application/json"
97
+ rescue Nylas::JsonParseError
98
+ handle_failed_response(result: result, response: response)
99
+ raise
100
+ end
96
101
 
97
102
  handle_failed_response(result: result, response: response)
98
103
  response
@@ -103,8 +108,8 @@ module Nylas
103
108
  also_log: { result: true, values: %i[method url path headers query payload] }
104
109
 
105
110
  def build_request(method:, path: nil, headers: {}, query: {}, payload: nil, timeout: nil)
106
- headers[:params] = query
107
111
  url ||= url_for_path(path)
112
+ url = add_query_params_to_url(url, query)
108
113
  resulting_headers = default_headers.merge(headers)
109
114
  { method: method, url: url, payload: payload, headers: resulting_headers, timeout: timeout }
110
115
  end
@@ -137,8 +142,9 @@ module Nylas
137
142
  @default_headers ||= {
138
143
  "X-Nylas-API-Wrapper" => "ruby",
139
144
  "X-Nylas-Client-Id" => @app_id,
145
+ "Nylas-API-Version" => SUPPORTED_API_VERSION,
140
146
  "User-Agent" => "Nylas Ruby SDK #{Nylas::VERSION} - #{RUBY_VERSION}",
141
- "Content-types" => "application/json"
147
+ "Content-type" => "application/json"
142
148
  }
143
149
  end
144
150
 
@@ -175,11 +181,40 @@ module Nylas
175
181
  end
176
182
 
177
183
  def handle_anticipated_failure_mode(http_code:, response:)
178
- return if http_code == 200
179
- return unless response.is_a?(Hash)
184
+ return if HTTP_SUCCESS_CODES.include?(http_code)
180
185
 
181
186
  exception = HTTP_CODE_TO_EXCEPTIONS.fetch(http_code, APIError)
182
- raise exception.new(response[:type], response[:message], response.fetch(:server_error, nil))
187
+ raise exception.new(http_code, response) unless response.is_a?(Hash)
188
+
189
+ raise exception.new(
190
+ response[:type],
191
+ response[:message],
192
+ response.fetch(:server_error, nil)
193
+ )
194
+ end
195
+
196
+ def add_query_params_to_url(url, query)
197
+ unless query.empty?
198
+ uri = URI.parse(url)
199
+ query = custom_params(query)
200
+ params = URI.decode_www_form(uri.query || "") + query.to_a
201
+ uri.query = URI.encode_www_form(params)
202
+ url = uri.to_s
203
+ end
204
+
205
+ url
206
+ end
207
+
208
+ def custom_params(query)
209
+ # Convert hash to "<key>:<value>" form for metadata_pair query
210
+ if query.key?(:metadata_pair)
211
+ pairs = query[:metadata_pair].map do |key, value|
212
+ "#{key}:#{value}"
213
+ end
214
+ query[:metadata_pair] = pairs
215
+ end
216
+
217
+ query
183
218
  end
184
219
  end
185
220
  end
data/lib/nylas/message.rb CHANGED
@@ -37,7 +37,7 @@ module Nylas
37
37
  attribute :folder, :folder
38
38
  attribute :folder_id, :string
39
39
 
40
- has_n_of_attribute :labels, :label, exclude_when: [:saving]
40
+ has_n_of_attribute :labels, :label, read_only: true
41
41
  has_n_of_attribute :label_ids, :string
42
42
 
43
43
  transfer :api, to: %i[events files folder labels]
@@ -67,6 +67,8 @@ module Nylas
67
67
  return self unless headers.nil?
68
68
 
69
69
  assign(**api.execute(method: :get, path: resource_path, query: { view: "expanded" }))
70
+ # Transfer reference to the API to attributes that need it
71
+ transfer_attributes
70
72
  self
71
73
  end
72
74
 
@@ -75,7 +77,7 @@ module Nylas
75
77
 
76
78
  execute(
77
79
  method: :put,
78
- payload: attributes.serialize(keys: allowed_keys_for_save),
80
+ payload: attributes.serialize_for_api,
79
81
  path: resource_path
80
82
  )
81
83
  end
data/lib/nylas/model.rb CHANGED
@@ -48,7 +48,7 @@ module Nylas
48
48
 
49
49
  execute(
50
50
  method: :post,
51
- payload: attributes.serialize,
51
+ payload: attributes.serialize_for_api,
52
52
  path: resources_path,
53
53
  query: query_params
54
54
  )
@@ -58,12 +58,37 @@ module Nylas
58
58
  raise ModelNotUpdatableError, model_class unless updatable?
59
59
 
60
60
  attributes.merge(**data)
61
- execute(
62
- method: :put,
63
- payload: attributes.serialize(keys: data.keys),
64
- path: resource_path,
65
- query: query_params
66
- )
61
+ payload = attributes.serialize_for_api(keys: data.keys)
62
+ update_call(payload)
63
+
64
+ true
65
+ rescue Registry::MissingKeyError => e
66
+ raise ModelMissingFieldError.new(e.key, self)
67
+ end
68
+
69
+ def save_all_attributes
70
+ result = if persisted?
71
+ raise ModelNotUpdatableError, self unless updatable?
72
+
73
+ execute(
74
+ method: :put,
75
+ payload: attributes.serialize_all_for_api,
76
+ path: resource_path
77
+ )
78
+ else
79
+ create
80
+ end
81
+
82
+ attributes.merge(result)
83
+ end
84
+
85
+ def update_all_attributes(**data)
86
+ raise ModelNotUpdatableError, model_class unless updatable?
87
+
88
+ attributes.merge(**data)
89
+ payload = attributes.serialize_all_for_api(keys: data.keys)
90
+ update_call(payload)
91
+
67
92
  true
68
93
  rescue Registry::MissingKeyError => e
69
94
  raise ModelMissingFieldError.new(e.key, self)
@@ -95,20 +120,24 @@ module Nylas
95
120
 
96
121
  private
97
122
 
98
- def allowed_keys_for_save
99
- attributes.attribute_definitions.to_h.reject do |_k, v|
100
- v.exclude_when.include?(:saving)
101
- end.keys
102
- end
103
-
104
123
  def save_call
105
124
  execute(
106
125
  method: :put,
107
- payload: attributes.serialize(keys: allowed_keys_for_save),
126
+ payload: attributes.serialize_for_api,
108
127
  path: resource_path
109
128
  )
110
129
  end
111
130
 
131
+ def update_call(payload)
132
+ result = execute(
133
+ method: :put,
134
+ payload: payload,
135
+ path: resource_path,
136
+ query: query_params
137
+ )
138
+ attributes.merge(result) if result
139
+ end
140
+
112
141
  def query_params
113
142
  {}
114
143
  end
@@ -34,17 +34,22 @@ module Nylas
34
34
  # Methods to call when tweaking Attributable classes
35
35
  module ClassMethods
36
36
  # rubocop:disable Naming/PredicateName
37
- def has_n_of_attribute(name, type_name, exclude_when: [], default: [])
38
- attribute_definitions[name] = ListAttributeDefinition.new(type_name: type_name,
39
- exclude_when: exclude_when,
40
- default: default)
37
+ def has_n_of_attribute(name, type_name, read_only: false, default: [])
38
+ attribute_definitions[name] = ListAttributeDefinition.new(
39
+ type_name: type_name,
40
+ read_only: read_only,
41
+ default: default
42
+ )
41
43
  define_accessors(name)
42
44
  end
43
45
  # rubocop:enable Naming/PredicateName
44
46
 
45
- def attribute(name, type_name, exclude_when: [], default: nil)
46
- attribute_definitions[name] = AttributeDefinition.new(type_name: type_name,
47
- exclude_when: exclude_when, default: default)
47
+ def attribute(name, type_name, read_only: false, default: nil)
48
+ attribute_definitions[name] = AttributeDefinition.new(
49
+ type_name: type_name,
50
+ read_only: read_only,
51
+ default: default
52
+ )
48
53
  define_accessors(name)
49
54
  end
50
55
 
@@ -58,6 +63,21 @@ module Nylas
58
63
  end
59
64
  end
60
65
 
66
+ # Allows a class to inherit parent's attributes
67
+ def inherit_attributes
68
+ return if superclass.nil?
69
+
70
+ parent_attributes = superclass.attribute_definitions
71
+ parent_attributes.each do |parent_attribute|
72
+ name = parent_attribute[0]
73
+ attr = parent_attribute[1]
74
+ next if attribute_definitions.key?(name)
75
+
76
+ attribute_definitions[name] = attr
77
+ define_accessors(name)
78
+ end
79
+ end
80
+
61
81
  def attribute_definitions
62
82
  @attribute_definitions ||= Registry.new
63
83
  end
@@ -6,11 +6,11 @@ module Nylas
6
6
  class AttributeDefinition
7
7
  extend Forwardable
8
8
  def_delegators :type, :cast, :serialize
9
- attr_accessor :type_name, :exclude_when, :default
9
+ attr_accessor :type_name, :read_only, :default
10
10
 
11
- def initialize(type_name:, exclude_when:, default:)
11
+ def initialize(type_name:, read_only:, default:)
12
12
  self.type_name = type_name
13
- self.exclude_when = exclude_when
13
+ self.read_only = read_only
14
14
  self.default = default
15
15
  end
16
16
 
@@ -39,6 +39,22 @@ module Nylas
39
39
  JSON.dump(to_h(keys: keys))
40
40
  end
41
41
 
42
+ def serialize_for_api(keys: attribute_definitions.keys)
43
+ api_keys = keys.delete_if { |key| attribute_definitions[key].read_only == true }
44
+
45
+ serialize(keys: api_keys)
46
+ end
47
+
48
+ def serialize_all_for_api(keys: attribute_definitions.keys)
49
+ api_keys = keys.delete_if { |key| attribute_definitions[key].read_only == true }
50
+
51
+ JSON.dump(
52
+ api_keys.each_with_object({}) do |key, casted_data|
53
+ casted_data[key] = attribute_definitions[key].serialize(self[key])
54
+ end
55
+ )
56
+ end
57
+
42
58
  private
43
59
 
44
60
  def cast(key, value)
@@ -4,11 +4,11 @@ module Nylas
4
4
  module Model
5
5
  # Allows models to have an attribute which is a lists of another type of thing
6
6
  class ListAttributeDefinition
7
- attr_accessor :type_name, :exclude_when, :default
7
+ attr_accessor :type_name, :read_only, :default
8
8
 
9
- def initialize(type_name:, exclude_when:, default:)
9
+ def initialize(type_name:, read_only:, default:)
10
10
  self.type_name = type_name
11
- self.exclude_when = exclude_when
11
+ self.read_only = read_only
12
12
  self.default = default
13
13
  end
14
14
 
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Class containing methods for accessing Neural API features.
5
+ # @see https://developer.nylas.com/docs/intelligence/
6
+ class Neural
7
+ attr_accessor :api
8
+
9
+ def initialize(api:)
10
+ self.api = api
11
+ end
12
+
13
+ def sentiment_analysis_message(message_ids)
14
+ body = { message_id: message_ids }
15
+ response = request(NeuralSentimentAnalysis.resources_path, body)
16
+
17
+ collection = []
18
+ response.each do |sentiment|
19
+ collection.push(NeuralSentimentAnalysis.new(**sentiment.merge(api: api)))
20
+ end
21
+ collection
22
+ end
23
+
24
+ def sentiment_analysis_text(text)
25
+ body = { text: text }
26
+ NeuralSentimentAnalysis.new(**request(NeuralSentimentAnalysis.resources_path, body).merge(api: api))
27
+ end
28
+
29
+ def extract_signature(message_ids, options = nil)
30
+ body = { message_id: message_ids }
31
+ body = body.merge(options) unless options.nil?
32
+ response = request(NeuralSignatureExtraction.resources_path, body)
33
+
34
+ collection = []
35
+ response.each do |signature|
36
+ collection.push(NeuralSignatureExtraction.new(**signature.merge(api: api)))
37
+ end
38
+ collection
39
+ end
40
+
41
+ def ocr_request(file_id, pages = nil)
42
+ body = { file_id: file_id }
43
+ body[:pages] = pages unless pages.nil?
44
+
45
+ NeuralOcr.new(**request(NeuralOcr.resources_path, body).merge(api: api))
46
+ end
47
+
48
+ def categorize(message_ids)
49
+ body = { message_id: message_ids }
50
+ response = request(NeuralCategorizer.resources_path, body)
51
+
52
+ collection = []
53
+ response.each do |categorize|
54
+ collection.push(NeuralCategorizer.new(**categorize.merge(api: api)))
55
+ end
56
+ collection
57
+ end
58
+
59
+ def clean_conversation(message_ids, options = nil)
60
+ body = { message_id: message_ids }
61
+ body = body.merge(delete_from_hash(options.to_hash, :parse_contact)) unless options.nil?
62
+
63
+ response = request(NeuralCleanConversation.resources_path, body)
64
+ collection = []
65
+ response.each do |conversation|
66
+ collection.push(NeuralCleanConversation.new(**conversation.merge(api: api)))
67
+ end
68
+ collection
69
+ end
70
+
71
+ private
72
+
73
+ def request(path, body)
74
+ api.execute(
75
+ method: :put,
76
+ path: path,
77
+ payload: JSON.dump(body)
78
+ )
79
+ end
80
+
81
+ # For Ruby < 3.0 support, as it doesn't support Hash.except
82
+ def delete_from_hash(hash, to_delete)
83
+ hash.delete(to_delete)
84
+ hash
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Structure to represent a the Neural Categorizer object.
5
+ # @see https://developer.nylas.com/docs/intelligence/categorizer/#categorize-message-response
6
+ class NeuralCategorizer < Message
7
+ include Model
8
+ self.resources_path = "/neural/categorize"
9
+ allows_operations(listable: true)
10
+
11
+ attribute :categorizer, :categorize
12
+ # Overrides Message's label attribute as currently categorize returns
13
+ # list of strings for labels instead of label object types
14
+ has_n_of_attribute :labels, :string
15
+
16
+ inherit_attributes
17
+
18
+ def recategorize(category)
19
+ body = { message_id: id, category: category }
20
+ api.execute(
21
+ method: :post,
22
+ path: "#{resources_path}/feedback",
23
+ payload: JSON.dump(body)
24
+ )
25
+ list = api.neural.categorize([id])
26
+ list[0]
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Structure to represent a the Neural Clean Conversations object.
5
+ # @see https://developer.nylas.com/docs/intelligence/clean-conversations/#clean-conversation-response
6
+ class NeuralCleanConversation < Message
7
+ include Model
8
+ self.resources_path = "/neural/conversation"
9
+ allows_operations(listable: true)
10
+ IMAGE_REGEX = /[(']cid:(.*?)[)']/.freeze
11
+
12
+ attribute :conversation, :string
13
+ attribute :model_version, :string
14
+
15
+ inherit_attributes
16
+
17
+ # Parses image file IDs found in the clean conversation object and returns
18
+ # an array of File objects returned from the File API
19
+ def extract_images
20
+ return if conversation.nil?
21
+
22
+ files = []
23
+ matches = conversation.scan(IMAGE_REGEX)
24
+ matches.each do |match|
25
+ # After applying the regex, if there are IDs found they would be
26
+ # in the form of => 'cid:xxxx' (including apostrophes), so we discard
27
+ # everything before and after the file ID (denoted as xxxx above)
28
+ files.push(api.files.find(match))
29
+ end
30
+ files
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Structure to represent the "Link" object in the Neural API's Signature Extraction Contact object
5
+ # @see https://developer.nylas.com/docs/intelligence/signature-extraction/#parse-signature-response
6
+ class NeuralContactLink
7
+ include Model::Attributable
8
+ attribute :description, :string
9
+ attribute :url, :string
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Structure to represent the "Name" object in the Neural API's Signature Extraction Contact object
5
+ # @see https://developer.nylas.com/docs/intelligence/signature-extraction/#parse-signature-response
6
+ class NeuralContactName
7
+ include Model::Attributable
8
+ attribute :first_name, :string
9
+ attribute :last_name, :string
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Structure to represent a the Neural Optical Character Recognition object.
5
+ # @see https://developer.nylas.com/docs/intelligence/optical-charecter-recognition/#ocr-response
6
+ class NeuralMessageOptions
7
+ attr_accessor :ignore_links, :ignore_images, :ignore_tables, :remove_conclusion_phrases,
8
+ :images_as_markdown, :parse_contact
9
+
10
+ def initialize(ignore_links: nil,
11
+ ignore_images: nil,
12
+ ignore_tables: nil,
13
+ remove_conclusion_phrases: nil,
14
+ images_as_markdown: nil,
15
+ parse_contact: nil)
16
+ @ignore_links = ignore_links
17
+ @ignore_images = ignore_images
18
+ @ignore_tables = ignore_tables
19
+ @remove_conclusion_phrases = remove_conclusion_phrases
20
+ @images_as_markdown = images_as_markdown
21
+ @parse_contact = parse_contact
22
+ end
23
+
24
+ def to_hash
25
+ hash = {}
26
+ hash[:ignore_links] = @ignore_links unless @ignore_links.nil?
27
+ hash[:ignore_images] = @ignore_images unless @ignore_images.nil?
28
+ hash[:ignore_tables] = @ignore_tables unless @ignore_tables.nil?
29
+ hash[:remove_conclusion_phrases] = @remove_conclusion_phrases unless @remove_conclusion_phrases.nil?
30
+ hash[:images_as_markdown] = @images_as_markdown unless @images_as_markdown.nil?
31
+ hash[:parse_contact] = @parse_contact unless @parse_contact.nil?
32
+ hash
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Structure to represent a the Neural Optical Character Recognition object.
5
+ # @see https://developer.nylas.com/docs/intelligence/optical-charecter-recognition/#ocr-response
6
+ class NeuralOcr < File
7
+ include Model
8
+ self.resources_path = "/neural/ocr"
9
+ allows_operations(listable: true)
10
+
11
+ has_n_of_attribute :ocr, :string
12
+ attribute :processed_pages, :integer
13
+
14
+ inherit_attributes
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Structure to represent a the Neural Sentiment Analysis object.
5
+ # @see https://developer.nylas.com/docs/intelligence/sentiment-analysis/#sentiment-analysis-response-message
6
+ class NeuralSentimentAnalysis
7
+ include Model
8
+ self.resources_path = "/neural/sentiment"
9
+ allows_operations(listable: true)
10
+
11
+ attribute :account_id, :string
12
+ attribute :sentiment, :string
13
+ attribute :sentiment_score, :float
14
+ attribute :processed_length, :integer
15
+ attribute :text, :string
16
+ end
17
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Structure to represent the Neural API's Signature Extraction Contact object
5
+ # @see https://developer.nylas.com/docs/intelligence/signature-extraction/#parse-signature-response
6
+ class NeuralSignatureContact
7
+ include Model::Attributable
8
+ has_n_of_attribute :job_titles, :string
9
+ has_n_of_attribute :links, :neural_contact_link
10
+ has_n_of_attribute :phone_numbers, :string
11
+ has_n_of_attribute :emails, :string
12
+ has_n_of_attribute :names, :neural_contact_name
13
+
14
+ attr_accessor :api
15
+
16
+ # Creates a Nylas contact object compatible with the contact endpoints.
17
+ # Please note if multiple names or multiple job titles were parsed only
18
+ # the first set are used.
19
+ def to_contact_object
20
+ contact = merge_multiple_hashes([convert_names, convert_emails, convert_phone_numbers, convert_links])
21
+ contact[:job_title] = job_titles[0] unless job_titles.nil?
22
+ Contact.new(**contact.merge(api: api))
23
+ end
24
+
25
+ private
26
+
27
+ def convert_names
28
+ return {} if names.empty?
29
+
30
+ contact = {}
31
+ contact[:given_name] = names[0].first_name if names[0].first_name
32
+ contact[:surname] = names[0].last_name if names[0].last_name
33
+ contact
34
+ end
35
+
36
+ def convert_emails
37
+ return {} if emails.empty?
38
+
39
+ contact = {}
40
+ contact[:emails] = []
41
+ emails.each do |e|
42
+ contact[:emails].push(type: "personal", email: e)
43
+ end
44
+ contact
45
+ end
46
+
47
+ def convert_phone_numbers
48
+ return {} if phone_numbers.empty?
49
+
50
+ contact = {}
51
+ contact[:phone_numbers] = []
52
+ phone_numbers.each do |number|
53
+ contact[:phone_numbers].push(type: "mobile", number: number)
54
+ end
55
+ contact
56
+ end
57
+
58
+ def convert_links
59
+ return {} if links.empty?
60
+
61
+ contact = {}
62
+ contact[:web_pages] = []
63
+ links.each do |link|
64
+ type = "homepage"
65
+ type = link.description unless link.description.empty?
66
+ contact[:web_pages].push(type: type, url: link.url)
67
+ end
68
+ contact
69
+ end
70
+
71
+ # For Ruby 2.5 support as it doesn't support multiple hashes to merge at once
72
+ def merge_multiple_hashes(hashes_to_merge)
73
+ hash = {}
74
+ hashes_to_merge.each do |new_hash|
75
+ hash = hash.merge(new_hash)
76
+ end
77
+
78
+ hash
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Structure to represent a the Signature Extraction Schema.
5
+ # @see https://developer.nylas.com/docs/intelligence/signature-extraction/#signature-feedback-response
6
+ class NeuralSignatureExtraction < Message
7
+ include Model
8
+ self.resources_path = "/neural/signature"
9
+
10
+ attribute :signature, :string
11
+ attribute :model_version, :string
12
+ attribute :contacts, :neural_signature_contact
13
+
14
+ inherit_attributes
15
+
16
+ transfer :api, to: %i[contacts]
17
+ end
18
+ end
@@ -26,7 +26,7 @@ module Nylas
26
26
  attribute :tracking, :message_tracking
27
27
 
28
28
  def send!
29
- Message.new(api.execute(method: :post, path: "/send", payload: to_json).merge(api: api))
29
+ Message.new(**api.execute(method: :post, path: "/send", payload: to_json).merge(api: api))
30
30
  end
31
31
  end
32
32
  end
@@ -11,8 +11,15 @@ module Nylas
11
11
  end
12
12
 
13
13
  def send!
14
- Message.new(api.execute(method: :post, path: "/send", payload: mime_compatible_string,
15
- headers: { "Content-type" => "message/rfc822" }).merge(api: api))
14
+ Message.new(**api.execute(
15
+ method: :post,
16
+ path: "/send",
17
+ payload: mime_compatible_string,
18
+ headers: HEADERS
19
+ ).merge(api: api))
16
20
  end
21
+
22
+ HEADERS = { "Content-type" => "message/rfc822" }.freeze
23
+ private_constant :HEADERS
17
24
  end
18
25
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nylas
4
+ # Ruby representation of a Nylas Room Resource object
5
+ # @see https://developer.nylas.com/docs/api/#tag--Room-Resources
6
+ class RoomResource
7
+ include Model
8
+ self.resources_path = "/resources"
9
+ allows_operations(listable: true)
10
+
11
+ attribute :object, :string, read_only: true
12
+ attribute :email, :string, read_only: true
13
+ attribute :name, :string, read_only: true
14
+ attribute :capacity, :string, read_only: true
15
+ attribute :building, :string, read_only: true
16
+ attribute :floor_name, :string, read_only: true
17
+ attribute :floor_number, :string, read_only: true
18
+ end
19
+ end
data/lib/nylas/rsvp.rb CHANGED
@@ -13,8 +13,12 @@ module Nylas
13
13
  attr_accessor :notify_participants
14
14
 
15
15
  def save
16
- api.execute(method: :post, path: "/send-rsvp", payload: attributes.serialize,
17
- query: { notify_participants: notify_participants })
16
+ api.execute(
17
+ method: :post,
18
+ path: "/send-rsvp",
19
+ payload: attributes.serialize_for_api,
20
+ query: { notify_participants: notify_participants }
21
+ )
18
22
  end
19
23
  end
20
24
  end
data/lib/nylas/thread.rb CHANGED
@@ -23,6 +23,7 @@ module Nylas
23
23
  has_n_of_attribute :labels, :label
24
24
  has_n_of_attribute :folders, :folder
25
25
  has_n_of_attribute :message_ids, :string
26
+ has_n_of_attribute :messages, :message
26
27
  has_n_of_attribute :participants, :participant
27
28
  attribute :snippet, :string
28
29
  attribute :starred, :boolean
data/lib/nylas/types.rb CHANGED
@@ -144,5 +144,16 @@ module Nylas
144
144
  end
145
145
  end
146
146
  Types.registry[:boolean] = BooleanType.new
147
+
148
+ # Type for attributes represented as floats.
149
+ class FloatType < ValueType
150
+ # @param value [Object] Strictly casts the passed in value to a boolean (must be true, not "" or 1)
151
+ def cast(value)
152
+ return nil if value.nil?
153
+
154
+ value.to_f
155
+ end
156
+ end
157
+ Types.registry[:float] = FloatType.new
147
158
  end
148
159
  end
data/lib/nylas/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nylas
4
- VERSION = "4.6.7"
4
+ VERSION = "5.3.0"
5
5
  end
data/lib/nylas/when.rb CHANGED
@@ -8,7 +8,7 @@ module Nylas
8
8
 
9
9
  include Model::Attributable
10
10
 
11
- attribute :object, :string
11
+ attribute :object, :string, read_only: true
12
12
 
13
13
  # when object == 'date'
14
14
  attribute :date, :date
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nylas
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.6.7
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nylas, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-22 00:00:00.000000000 Z
11
+ date: 2021-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -270,6 +270,7 @@ files:
270
270
  - lib/nylas/account.rb
271
271
  - lib/nylas/api.rb
272
272
  - lib/nylas/calendar.rb
273
+ - lib/nylas/categorize.rb
273
274
  - lib/nylas/collection.rb
274
275
  - lib/nylas/constraints.rb
275
276
  - lib/nylas/contact.rb
@@ -302,6 +303,16 @@ files:
302
303
  - lib/nylas/model/list_attribute_definition.rb
303
304
  - lib/nylas/model/transferable.rb
304
305
  - lib/nylas/native_authentication.rb
306
+ - lib/nylas/neural.rb
307
+ - lib/nylas/neural_categorizer.rb
308
+ - lib/nylas/neural_clean_conversation.rb
309
+ - lib/nylas/neural_contact_link.rb
310
+ - lib/nylas/neural_contact_name.rb
311
+ - lib/nylas/neural_message_options.rb
312
+ - lib/nylas/neural_ocr.rb
313
+ - lib/nylas/neural_sentiment_analysis.rb
314
+ - lib/nylas/neural_signature_contact.rb
315
+ - lib/nylas/neural_signature_extraction.rb
305
316
  - lib/nylas/new_message.rb
306
317
  - lib/nylas/nylas_date.rb
307
318
  - lib/nylas/participant.rb
@@ -310,6 +321,7 @@ files:
310
321
  - lib/nylas/raw_message.rb
311
322
  - lib/nylas/recurrence.rb
312
323
  - lib/nylas/registry.rb
324
+ - lib/nylas/room_resource.rb
313
325
  - lib/nylas/rsvp.rb
314
326
  - lib/nylas/search_collection.rb
315
327
  - lib/nylas/thread.rb
@@ -345,7 +357,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
345
357
  - !ruby/object:Gem::Version
346
358
  version: '0'
347
359
  requirements: []
348
- rubygems_version: 3.1.2
360
+ rubygems_version: 3.2.17
349
361
  signing_key:
350
362
  specification_version: 4
351
363
  summary: Gem for interacting with the Nylas API