chartmogul-ruby 4.14.0 → 4.15.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: 61fce0fe848312e4a28d7cc9cf5baf8bf62bf7a92da434789bfbffa3099c78ef
4
- data.tar.gz: 26ab5587c9a0d6c4dc543aca4ff558d4bbe9a89d3e309642c0b933cae2f81433
3
+ metadata.gz: 1d40707d3a05b63c0ecadcd2a871c769840ebe77182aae281e024167b6f73337
4
+ data.tar.gz: 89ce86fbbeacdaa4236d00c5a4c2d24926ece3fb1c71b4504b10a6efd1a55f95
5
5
  SHA512:
6
- metadata.gz: b28f66fa79c6ac41497911e0a2811ce4e419c7ec9a143d0acc8adbe85c5ef21ec71c87533855cf5cf119f0a3fdef519ca1717e0f4cc712c258ac35b363f7dd19
7
- data.tar.gz: b6ce9397538e1d1cad6c799e697c4ee4d0a33b2de8d31d628bc6cfe9d34e1902f2f5257ecf343ca4e316cb828e8c4615e1208dc3bb25ce5265716c7c50131f08
6
+ metadata.gz: 77f1fd6beaea8d60c4b4eb2a83a10fadb39ad67886a8a893340644de2864a56900e598a99a97b81b6700c465b8a201d320b5406b73bf86e470307fa80d771cde
7
+ data.tar.gz: f34020017620ea6d47992fef1a74db9863929cc98c6ebf3fc5f57206779de1e4f756ab39402af88704b39077873f878336dafc0bc677f48f7157023e1b36923f
@@ -16,7 +16,8 @@ module ChartMogul
16
16
  end
17
17
  end
18
18
 
19
- json = ChartMogul::Utils::JSONParser.parse(resp.body, immutable_keys: self.class.immutable_keys)
19
+ parsed_body = resp.body.empty? ? '{}' : resp.body
20
+ json = ChartMogul::Utils::JSONParser.parse(parsed_body, immutable_keys: self.class.immutable_keys)
20
21
 
21
22
  assign_all_attributes(json)
22
23
  end
@@ -31,7 +32,8 @@ module ChartMogul
31
32
  req.body = JSON.dump(resource.serialize_for_write)
32
33
  end
33
34
  end
34
- json = ChartMogul::Utils::JSONParser.parse(resp.body, immutable_keys: immutable_keys)
35
+ parsed_body = resp.body.empty? ? '{}' : resp.body
36
+ json = ChartMogul::Utils::JSONParser.parse(parsed_body, immutable_keys: immutable_keys)
35
37
 
36
38
  new_from_json(json)
37
39
  end
@@ -7,6 +7,7 @@ module ChartMogul
7
7
  set_immutable_keys([:custom])
8
8
 
9
9
  readonly_attr :uuid
10
+ readonly_attr :historical_values
10
11
 
11
12
  writeable_attr :customer_uuid
12
13
  writeable_attr :data_source_uuid
@@ -20,8 +21,10 @@ module ChartMogul
20
21
  writeable_attr :linked_in
21
22
  writeable_attr :twitter
22
23
  writeable_attr :notes
24
+ writeable_attr :last_seen
23
25
  writeable_attr :external_id
24
26
  writeable_attr :custom
27
+ writeable_attr :overrides
25
28
 
26
29
  include API::Actions::Create
27
30
  include API::Actions::Custom
@@ -38,6 +41,22 @@ module ChartMogul
38
41
  true
39
42
  end
40
43
 
44
+ def tasks(options = {})
45
+ Tasks.all(options.merge(contact_uuid: uuid))
46
+ end
47
+
48
+ def create_task(options = {})
49
+ Task.create!(with_associated_object_identifier(options))
50
+ end
51
+
52
+ def entity_notes(options = {})
53
+ EntityNotes.all(options.merge(contact_uuid: uuid))
54
+ end
55
+
56
+ def create_entity_note(options = {})
57
+ EntityNote.create!(with_associated_object_identifier(options))
58
+ end
59
+
41
60
  def serialize_for_write
42
61
  super.tap do |attributes|
43
62
  attributes.clone.each do |attribute_name, attribute_value|
@@ -55,6 +74,42 @@ module ChartMogul
55
74
  attributes[:external_id] = nil if instance_variable_defined?(:@external_id) && external_id.nil?
56
75
  end
57
76
  end
77
+
78
+ # Responses echo the current override state, so only overrides assigned by
79
+ # the caller are serialized: replaying an echoed `true` flag on a later
80
+ # update! would re-protect stale in-memory values on the server. The echoed
81
+ # hash is deep-frozen and `dup` copies only the top level — request changes
82
+ # by assigning a freshly built hash, not by editing or duplicating the
83
+ # reader's result. Assigned intent is cleared by the response
84
+ # of the successful write (every write endpoint echoes `overrides`); an
85
+ # endpoint that stopped echoing would leave it set and resend it on the
86
+ # next update!.
87
+ def overrides=(value)
88
+ @overrides_requested = true
89
+ @overrides = value
90
+ end
91
+
92
+ def serialize_overrides
93
+ overrides if @overrides_requested
94
+ end
95
+
96
+ private
97
+
98
+ def set_overrides(value)
99
+ @overrides_requested = false
100
+ @overrides = freeze_nested(value)
101
+ end
102
+
103
+ def freeze_nested(value)
104
+ value.each_value { |nested| freeze_nested(nested) } if value.is_a?(Hash)
105
+ value.freeze
106
+ end
107
+
108
+ def with_associated_object_identifier(options)
109
+ return options if options[:customer_uuid] || options[:associated_object_identifier]
110
+
111
+ options.merge(associated_object_identifier: { associated_object: 'contact', method: 'uuid', value: uuid })
112
+ end
58
113
  end
59
114
 
60
115
  class Contacts < APIResource
@@ -5,7 +5,7 @@ module ChartMogul
5
5
  class Customer < APIResource
6
6
  set_resource_name 'Customer'
7
7
  set_resource_path '/v1/customers'
8
- set_immutable_keys(%i[attributes custom])
8
+ set_immutable_keys(%i[attributes custom overrides historical_values])
9
9
 
10
10
  readonly_attr :uuid
11
11
  readonly_attr :id
@@ -22,6 +22,7 @@ module ChartMogul
22
22
  readonly_attr :currency_sign
23
23
  readonly_attr :data_source_uuids
24
24
  readonly_attr :external_ids
25
+ readonly_attr :historical_values
25
26
 
26
27
  writeable_attr :attributes
27
28
  writeable_attr :external_id
@@ -38,6 +39,7 @@ module ChartMogul
38
39
  writeable_attr :owner
39
40
  writeable_attr :primary_contact
40
41
  writeable_attr :website_url
42
+ writeable_attr :overrides
41
43
 
42
44
  include API::Actions::Create
43
45
  include API::Actions::Custom
@@ -57,16 +59,20 @@ module ChartMogul
57
59
  all(external_id: external_id).first
58
60
  end
59
61
 
60
- def self.retrieve_attributes(customer_uuid)
61
- custom_without_assign!(:get, "/v1/customers/#{customer_uuid}/attributes")
62
+ def self.retrieve_attributes(customer_uuid, options = {})
63
+ path = ChartMogul::ResourcePath.new("/v1/customers/#{customer_uuid}/attributes")
64
+ custom_without_assign!(:get, path.apply_with_get_params(options))
62
65
  end
63
66
 
64
67
  def self.add_tags_by_email!(email, *tags)
65
68
  custom_without_assign!(:post, '/v1/customers/attributes/tags', email: email, tags: tags)
66
69
  end
67
70
 
68
- def self.add_custom_attributes_by_email!(email, *custom_attrs)
69
- custom_without_assign!(:post, '/v1/customers/attributes/custom', email: email, custom: custom_attrs)
71
+ def self.add_custom_attributes_by_email!(email, *custom_attrs, overrides: nil, **brace_less_attr)
72
+ custom_attrs << brace_less_attr unless brace_less_attr.empty?
73
+ body = { email: email, custom: custom_attrs }
74
+ body[:overrides] = overrides if overrides
75
+ custom_without_assign!(:post, '/v1/customers/attributes/custom', body)
70
76
  end
71
77
 
72
78
  def self.merge!(into_uuid:, from_uuid:)
@@ -109,14 +115,26 @@ module ChartMogul
109
115
  Contact.create!(options.merge(customer_uuid: uuid))
110
116
  end
111
117
 
118
+ # @deprecated Use {#entity_notes} instead.
112
119
  def notes(options = {})
120
+ warn 'DEPRECATION WARNING: the method ChartMogul::Customer#notes is deprecated. Use ChartMogul::Customer#entity_notes instead.'
113
121
  Notes.all(options.merge(customer_uuid: uuid))
114
122
  end
115
123
 
124
+ # @deprecated Use {#create_entity_note} instead.
116
125
  def create_note(options = {})
126
+ warn 'DEPRECATION WARNING: the method ChartMogul::Customer#create_note is deprecated. Use ChartMogul::Customer#create_entity_note instead.'
117
127
  Note.create!(options.merge(customer_uuid: uuid))
118
128
  end
119
129
 
130
+ def entity_notes(options = {})
131
+ EntityNotes.all(options.merge(customer_uuid: uuid))
132
+ end
133
+
134
+ def create_entity_note(options = {})
135
+ EntityNote.create!(options.merge(customer_uuid: uuid))
136
+ end
137
+
120
138
  def opportunities(options = {})
121
139
  Opportnities.all(options.merge(customer_uuid: uuid))
122
140
  end
@@ -154,26 +172,49 @@ module ChartMogul
154
172
  tags: tags)[:tags]
155
173
  end
156
174
 
157
- def add_custom_attributes!(*custom_attrs)
158
- self.custom_attributes = custom_without_assign!(:post,
159
- "/v1/customers/#{uuid}/attributes/custom",
160
- custom: custom_attrs)[:custom]
161
- end
162
-
163
- def update_custom_attributes!(custom_attrs = {})
164
- self.custom_attributes = custom_without_assign!(:put,
165
- "/v1/customers/#{uuid}/attributes/custom",
166
- custom: custom_attrs)[:custom]
167
- end
168
-
169
- def remove_custom_attributes!(*custom_attrs)
170
- self.custom_attributes = custom_without_assign!(:delete,
171
- "/v1/customers/#{uuid}/attributes/custom",
172
- custom: custom_attrs)
173
- end
174
-
175
- def retrieve_attributes
176
- custom_without_assign!(:get, "/v1/customers/#{uuid}/attributes")
175
+ # The custom attribute helpers (including .add_custom_attributes_by_email!)
176
+ # take override flags via the overrides: keyword, shaped
177
+ # { custom: { Name: true } } — unlike create!/update!, where the same flags
178
+ # nest under { attributes: { custom: { Name: true } } }. In
179
+ # update_custom_attributes!, an attribute literally named `overrides` must
180
+ # be passed in a braced hash, since the bare keyword is taken:
181
+ # update_custom_attributes!({ overrides: 'value' }) updates the attribute
182
+ # update_custom_attributes!(overrides: { ... }) sends override flags
183
+ def add_custom_attributes!(*custom_attrs, overrides: nil, **brace_less_attr)
184
+ custom_attrs << brace_less_attr unless brace_less_attr.empty?
185
+ body = { custom: custom_attrs }
186
+ body[:overrides] = overrides if overrides
187
+ response = custom_without_assign!(:post,
188
+ "/v1/customers/#{uuid}/attributes/custom",
189
+ body)
190
+ sync_custom_attribute_overrides(response)
191
+ self.custom_attributes = response[:custom]
192
+ end
193
+
194
+ def update_custom_attributes!(custom_attrs = {}, overrides: nil, **brace_less_attrs)
195
+ body = { custom: custom_attrs.merge(brace_less_attrs) }
196
+ body[:overrides] = overrides if overrides
197
+ response = custom_without_assign!(:put,
198
+ "/v1/customers/#{uuid}/attributes/custom",
199
+ body)
200
+ sync_custom_attribute_overrides(response)
201
+ self.custom_attributes = response[:custom]
202
+ end
203
+
204
+ def remove_custom_attributes!(*custom_attrs, overrides: nil, **brace_less_attr)
205
+ custom_attrs << brace_less_attr unless brace_less_attr.empty?
206
+ body = { custom: custom_attrs }
207
+ body[:overrides] = overrides if overrides
208
+ response = custom_without_assign!(:delete,
209
+ "/v1/customers/#{uuid}/attributes/custom",
210
+ body)
211
+ sync_custom_attribute_overrides(response)
212
+ self.custom_attributes = response[:custom]
213
+ response
214
+ end
215
+
216
+ def retrieve_attributes(options = {})
217
+ self.class.retrieve_attributes(uuid, options)
177
218
  end
178
219
 
179
220
  def merge_into!(other_customer)
@@ -194,8 +235,49 @@ module ChartMogul
194
235
  )
195
236
  end
196
237
 
238
+ # Responses echo the current override state, so only overrides assigned by
239
+ # the caller are serialized: replaying an echoed `true` flag on a later
240
+ # update! would re-protect stale in-memory values on the server. The echoed
241
+ # hash is deep-frozen and `dup` copies only the top level — request changes
242
+ # by assigning a freshly built hash, not by editing or duplicating the
243
+ # reader's result. Assigned intent is cleared by the response
244
+ # of the successful write (every write endpoint echoes `overrides`); an
245
+ # endpoint that stopped echoing would leave it set and resend it on the
246
+ # next update!.
247
+ def overrides=(value)
248
+ @overrides_requested = true
249
+ @overrides = value
250
+ end
251
+
252
+ def serialize_overrides
253
+ overrides if @overrides_requested
254
+ end
255
+
197
256
  private
198
257
 
258
+ def set_overrides(value)
259
+ @overrides_requested = false
260
+ @overrides = freeze_nested(value)
261
+ end
262
+
263
+ def freeze_nested(value)
264
+ value.each_value { |nested| freeze_nested(nested) } if value.is_a?(Hash)
265
+ value.freeze
266
+ end
267
+
268
+ # The custom attribute endpoints echo the customer's current custom
269
+ # attribute overrides, in the shape overrides[:attributes] uses. Synced
270
+ # through the read-only path so the flags are never marked for
271
+ # serialization; a pending explicitly assigned hash is not clobbered,
272
+ # keeping the serialize-exactly-once contract.
273
+ def sync_custom_attribute_overrides(response)
274
+ return if @overrides_requested || !response.key?(:overrides)
275
+
276
+ updated = (overrides || {}).reject { |key, _| key == :attributes }
277
+ updated[:attributes] = response[:overrides] unless response[:overrides].empty?
278
+ set_overrides(updated)
279
+ end
280
+
199
281
  def tags=(tags)
200
282
  @attributes[:tags] = tags
201
283
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChartMogul
4
+ class EntityNote < APIResource
5
+ set_resource_name 'EntityNote'
6
+ set_resource_path '/v1/notes'
7
+
8
+ readonly_attr :uuid
9
+ readonly_attr :associated_object
10
+ readonly_attr :associated_object_uuid
11
+ readonly_attr :author
12
+
13
+ writeable_attr :customer_uuid
14
+ writeable_attr :associated_object_identifier
15
+ writeable_attr :type
16
+ writeable_attr :text
17
+ writeable_attr :author_email
18
+ writeable_attr :call_duration
19
+ writeable_attr :created_at
20
+ writeable_attr :updated_at
21
+
22
+ include API::Actions::Create
23
+ include API::Actions::Destroy
24
+ include API::Actions::Retrieve
25
+ include API::Actions::Update
26
+
27
+ def self.all(options = {})
28
+ EntityNotes.all(options)
29
+ end
30
+ end
31
+
32
+ class EntityNotes < APIResource
33
+ set_resource_name 'EntityNotes'
34
+ set_resource_path '/v1/notes'
35
+
36
+ include Concerns::Entries
37
+ include Concerns::PageableWithCursor
38
+
39
+ set_entry_class EntityNote
40
+ end
41
+ end
@@ -3,6 +3,7 @@
3
3
  require 'forwardable'
4
4
 
5
5
  module ChartMogul
6
+ # @deprecated Use {ChartMogul::EntityNote} instead. /v1/customer_notes is superseded by /v1/notes.
6
7
  class Note < APIResource
7
8
  set_resource_name 'CustomerNote'
8
9
  set_resource_path '/v1/customer_notes'
@@ -23,11 +24,55 @@ module ChartMogul
23
24
  include API::Actions::Retrieve
24
25
  include API::Actions::Update
25
26
 
27
+ # @deprecated Use {ChartMogul::EntityNote.all} instead.
26
28
  def self.all(options = {})
27
29
  Notes.all(options)
28
30
  end
31
+
32
+ # @deprecated Use {ChartMogul::EntityNote.create!} instead.
33
+ def self.create!(attributes = {})
34
+ warn 'DEPRECATION WARNING: the method ChartMogul::Note.create! is deprecated. Use ChartMogul::EntityNote.create! instead.'
35
+ super
36
+ end
37
+
38
+ # @deprecated Use {ChartMogul::EntityNote.retrieve} instead.
39
+ def self.retrieve(uuid, options = {})
40
+ warn 'DEPRECATION WARNING: the method ChartMogul::Note.retrieve is deprecated. Use ChartMogul::EntityNote.retrieve instead.'
41
+ super
42
+ end
43
+
44
+ # @deprecated Use {ChartMogul::EntityNote.update!} instead.
45
+ def self.update!(uuid, attributes = {})
46
+ warn 'DEPRECATION WARNING: the method ChartMogul::Note.update! is deprecated. Use ChartMogul::EntityNote.update! instead.'
47
+ super
48
+ end
49
+
50
+ # @deprecated Use {ChartMogul::EntityNote.destroy!} instead.
51
+ def self.destroy!(options = {})
52
+ warn 'DEPRECATION WARNING: the method ChartMogul::Note.destroy! is deprecated. Use ChartMogul::EntityNote.destroy! instead.'
53
+ super
54
+ end
55
+
56
+ # @deprecated Use {ChartMogul::EntityNote#create!} instead.
57
+ def create!
58
+ warn 'DEPRECATION WARNING: the method ChartMogul::Note#create! is deprecated. Use ChartMogul::EntityNote#create! instead.'
59
+ super
60
+ end
61
+
62
+ # @deprecated Use {ChartMogul::EntityNote#update!} instead.
63
+ def update!
64
+ warn 'DEPRECATION WARNING: the method ChartMogul::Note#update! is deprecated. Use ChartMogul::EntityNote#update! instead.'
65
+ super
66
+ end
67
+
68
+ # @deprecated Use {ChartMogul::EntityNote#destroy!} instead.
69
+ def destroy!
70
+ warn 'DEPRECATION WARNING: the method ChartMogul::Note#destroy! is deprecated. Use ChartMogul::EntityNote#destroy! instead.'
71
+ super
72
+ end
29
73
  end
30
74
 
75
+ # @deprecated Use {ChartMogul::EntityNotes} instead.
31
76
  class Notes < APIResource
32
77
  set_resource_name 'CustomerNotes'
33
78
  set_resource_path '/v1/customer_notes'
@@ -36,5 +81,11 @@ module ChartMogul
36
81
  include Concerns::PageableWithCursor
37
82
 
38
83
  set_entry_class Note
84
+
85
+ # @deprecated Use {ChartMogul::EntityNotes.all} instead.
86
+ def self.all(options = {})
87
+ warn 'DEPRECATION WARNING: the method ChartMogul::Notes.all is deprecated. Use ChartMogul::EntityNotes.all instead.'
88
+ super
89
+ end
39
90
  end
40
91
  end
@@ -42,6 +42,7 @@ module ChartMogul
42
42
  end
43
43
 
44
44
  def self.all(customer_uuid, options = {})
45
+ warn 'DEPRECATION WARNING: the method ChartMogul::Subscription.all is deprecated. Use ChartMogul::Metrics::Customers::Subscriptions.all instead.'
45
46
  Subscriptions.all(customer_uuid, options)
46
47
  end
47
48
 
@@ -71,6 +72,7 @@ module ChartMogul
71
72
  set_entry_class Subscription
72
73
 
73
74
  def self.all(customer_uuid, options = {})
75
+ warn 'DEPRECATION WARNING: the method ChartMogul::Subscriptions.all is deprecated. Use ChartMogul::Metrics::Customers::Subscriptions.all instead.'
74
76
  super(options.merge(customer_uuid: customer_uuid))
75
77
  end
76
78
 
@@ -8,10 +8,13 @@ module ChartMogul
8
8
  set_resource_path '/v1/tasks'
9
9
 
10
10
  readonly_attr :task_uuid
11
+ readonly_attr :associated_object
12
+ readonly_attr :associated_object_uuid
11
13
  readonly_attr :created_at
12
14
  readonly_attr :updated_at
13
15
 
14
16
  writeable_attr :customer_uuid
17
+ writeable_attr :associated_object_identifier
15
18
  writeable_attr :task_details
16
19
  writeable_attr :assignee
17
20
  writeable_attr :due_date
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ChartMogul
4
- VERSION = '4.14.0'
4
+ VERSION = '4.15.0'
5
5
  end
data/lib/chartmogul.rb CHANGED
@@ -60,6 +60,7 @@ require 'chartmogul/invoice'
60
60
  require 'chartmogul/customer_invoices'
61
61
  require 'chartmogul/customer'
62
62
  require 'chartmogul/note'
63
+ require 'chartmogul/entity_note'
63
64
  require 'chartmogul/contact'
64
65
  require 'chartmogul/data_source'
65
66
  require 'chartmogul/data_source_settings/auto_churn_subscription_setting'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chartmogul-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.14.0
4
+ version: 4.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Petr Kopac
@@ -230,6 +230,7 @@ files:
230
230
  - lib/chartmogul/data_source_settings/auto_churn_subscription_setting.rb
231
231
  - lib/chartmogul/data_source_settings/processing_status.rb
232
232
  - lib/chartmogul/enrichment/customer.rb
233
+ - lib/chartmogul/entity_note.rb
233
234
  - lib/chartmogul/errors/chartmogul_error.rb
234
235
  - lib/chartmogul/errors/configuration_error.rb
235
236
  - lib/chartmogul/errors/deprecated_parameter_error.rb