chartmogul-ruby 4.7.1 → 4.9.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: 80466b596764090a0a5b98d57f27ac68434832c70669946bce93db4f8bb6c5e0
4
- data.tar.gz: b67f72518317c3e8ca975752ca5ce479697bb41a4a8741e443c4503d015295c6
3
+ metadata.gz: 206aab68993f235a7a3c98362163e04bb2d0026946631adfa4714b87d5f475bc
4
+ data.tar.gz: 33357f48412b21e88a6baca2063f8b6b6fc46f1b37eebaef74b595c0c03debb4
5
5
  SHA512:
6
- metadata.gz: af3b79f2c5556630f406ca2ecfc386a5791f391925bbc7798c6a0c60e0f9e24bfafa73f39cba5ad028a8454c01732c6cd0240e68f82e0bf2b2a7952fca208797
7
- data.tar.gz: be695715d4eebdb77f5adc68669ee5e833a9e02f84e87ee324b1a51f52ff78b41014f941a87c2a71a36050cb3eb6c0db8a5df0c45c97d2b8480dfc1ec839e078
6
+ metadata.gz: df56e58e7163aef6e332b98ade81835e4a93f57a93d3883524a883313cd858ce55c6238d4782bd2688acd6e41f9ade30534a404ea71aa87052c9a41a2f6bf81e
7
+ data.tar.gz: 583f6aa39696e49d80b62ac7d5d082d51ffd8065c893b318e0fa50b7ace9f66c32f1eb41af00bc62e8b44a5a638ab5ef11d8268be986c6b73864da97ce740772
@@ -9,7 +9,7 @@ jobs:
9
9
  runs-on: ubuntu-latest
10
10
  strategy:
11
11
  matrix:
12
- ruby-version: [2.7, 3.0, 3.1, 3.2, 3.3, head]
12
+ ruby-version: [3.2, 3.3, 3.4, head]
13
13
  steps:
14
14
  - uses: actions/checkout@v2
15
15
  - name: Set up Ruby ${{ matrix.ruby-version }}
data/README.md CHANGED
@@ -48,7 +48,7 @@ Or install it yourself as:
48
48
 
49
49
  ### Supported Ruby Versions
50
50
 
51
- This gem supports Ruby 2.7 and above.
51
+ This gem supports Ruby 3.2 and above.
52
52
 
53
53
  ## Configuration
54
54
 
data/changelog.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # chartmogul-ruby Change Log
2
2
 
3
+ ## Version 4.9.0 - Nov 25, 2025
4
+ - Add field subscription-set-external-id to /activities, drop support for eol rubies
5
+
3
6
  ## Version 4.7.1 - July 25, 2025
4
7
  - Fix create! and update! methods for Task (customer_uuid was omitted from payload)
5
8
 
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.description = 'Official Ruby client for ChartMogul\'s API'
15
15
  spec.homepage = 'https://github.com/chartmogul/chartmogul-ruby'
16
16
  spec.license = 'MIT'
17
- spec.required_ruby_version = '>= 2.7'
17
+ spec.required_ruby_version = '>= 3.2'
18
18
 
19
19
  spec.post_install_message = %q{
20
20
  Starting October 29 2021, we are updating our developer libraries to support the enhanced API Access Management. Please use the same API Key for both API Token and Secret Key.
@@ -30,16 +30,14 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency 'faraday', '~> 2.8'
31
31
  spec.add_dependency 'faraday-retry', '~> 2.2'
32
32
 
33
- # Higher versions break ruby 2.7 support.
34
- spec.add_development_dependency 'bundler', '~> 2'
35
33
  spec.add_development_dependency 'cgi'
36
34
  spec.add_development_dependency 'pry'
37
35
  spec.add_development_dependency 'rake'
38
36
  spec.add_development_dependency 'rspec'
39
- spec.add_development_dependency 'rubocop', '= 1.56.3'
40
- spec.add_development_dependency 'rubocop-rspec', '~> 2.9'
41
- spec.add_development_dependency 'rubocop-thread_safety', '~> 0.5.1'
42
- spec.add_development_dependency 'simplecov', '~> 0.21'
37
+ spec.add_development_dependency 'rubocop'
38
+ spec.add_development_dependency 'rubocop-rspec'
39
+ spec.add_development_dependency 'rubocop-thread_safety'
40
+ spec.add_development_dependency 'simplecov'
43
41
  spec.add_development_dependency 'vcr'
44
42
  spec.add_development_dependency 'webmock'
45
43
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
3
+ require "forwardable"
4
+ require "set"
4
5
 
5
6
  module ChartMogul
6
7
  class APIResource < ChartMogul::Object
@@ -8,17 +9,45 @@ module ChartMogul
8
9
 
9
10
  RETRY_STATUSES = [429, *500..599].freeze
10
11
  RETRY_EXCEPTIONS = [
11
- 'Faraday::ConnectionFailed',
12
- 'Faraday::RetriableResponse'
12
+ "Faraday::ConnectionFailed",
13
+ "Faraday::RetriableResponse"
13
14
  ].freeze
14
15
  BACKOFF_FACTOR = 2
15
16
  INTERVAL_RANDOMNESS = 0.5
16
17
  INTERVAL = 1
17
18
  MAX_INTERVAL = 60
18
- THREAD_CONNECTION_KEY = 'chartmogul_ruby.api_resource.connection'
19
+ THREAD_CONNECTION_KEY = "chartmogul_ruby.api_resource.connection"
19
20
 
20
21
  class << self; attr_reader :resource_path, :resource_name, :resource_root_key end
21
22
 
23
+ def self.query_params
24
+ @query_params ||= Set.new
25
+ end
26
+
27
+ def self.writeable_query_param(attribute, options = {})
28
+ query_params << attribute.to_sym
29
+ writeable_attr(attribute, options)
30
+ end
31
+
32
+ def self.extract_query_params(attrs, resource_key = nil)
33
+ remaining_attrs = attrs.dup
34
+ query_params = {}
35
+
36
+ self.query_params.each do |param|
37
+ # If resource_key is specified, look in nested structure
38
+ if resource_key && remaining_attrs[resource_key].is_a?(Hash) &&
39
+ remaining_attrs[resource_key]&.key?(param) &&
40
+ remaining_attrs[resource_key][param]
41
+ query_params[param] = remaining_attrs[resource_key].delete(param)
42
+ # Otherwise look at top level
43
+ elsif remaining_attrs.key?(param) && remaining_attrs[param]
44
+ query_params[param] = remaining_attrs.delete(param)
45
+ end
46
+ end
47
+
48
+ [remaining_attrs, query_params]
49
+ end
50
+
22
51
  def self.set_resource_path(path)
23
52
  @resource_path = ChartMogul::ResourcePath.new(path)
24
53
  end
@@ -60,10 +89,10 @@ module ChartMogul
60
89
  message = "JSON schema validation hasn't passed."
61
90
  raise ChartMogul::SchemaInvalidError.new(message, http_status: 400, response: response)
62
91
  when 401
63
- message = 'No valid API key provided'
92
+ message = "No valid API key provided"
64
93
  raise ChartMogul::UnauthorizedError.new(message, http_status: 401, response: response)
65
94
  when 403
66
- message = 'The requested action is forbidden.'
95
+ message = "The requested action is forbidden."
67
96
  raise ChartMogul::ForbiddenError.new(message, http_status: 403, response: response)
68
97
  when 404
69
98
  message = "The requested #{resource_name} could not be found."
@@ -72,7 +101,7 @@ module ChartMogul
72
101
  message = "The #{resource_name} could not be created or updated."
73
102
  raise ChartMogul::ResourceInvalidError.new(message, http_status: 422, response: response)
74
103
  when 500..504
75
- message = 'ChartMogul API server response error'
104
+ message = "ChartMogul API server response error"
76
105
  raise ChartMogul::ServerError.new(message, http_status: http_status, response: response)
77
106
  else
78
107
  message = "#{resource_name} request error has occurred."
@@ -84,17 +113,63 @@ module ChartMogul
84
113
  raise ChartMogul::ChartMogulError, exception.message
85
114
  end
86
115
 
87
- def_delegators 'self.class', :resource_path, :resource_name, :resource_root_key, :connection, :handling_errors
116
+ def_delegators "self.class", :resource_path, :resource_name, :resource_root_key, :connection, :handling_errors
117
+
118
+ def extract_query_params(attrs, resource_key = nil)
119
+ remaining_attrs = attrs.dup
120
+ query_params = {}
121
+
122
+ self.class.query_params.each do |param|
123
+ # If resource_key is specified, look in nested structure
124
+ if resource_key && remaining_attrs[resource_key].is_a?(Hash) &&
125
+ remaining_attrs[resource_key]&.key?(param) &&
126
+ remaining_attrs[resource_key][param]
127
+ query_params[param] = remaining_attrs[resource_key].delete(param)
128
+ # Otherwise look at top level
129
+ elsif remaining_attrs.key?(param) && remaining_attrs[param]
130
+ query_params[param] = remaining_attrs.delete(param)
131
+ end
132
+ end
133
+
134
+ [remaining_attrs, query_params]
135
+ end
136
+
137
+ # Generate path with query parameters applied
138
+ def path_with_query_params(attrs)
139
+ _, query_params = extract_query_params(attrs)
140
+ query_params.empty? ? resource_path.path : resource_path.apply_with_get_params(query_params)
141
+ end
142
+
143
+ # Enhanced custom! that automatically handles query parameters
144
+ def custom_with_query_params!(http_method, body_data = {}, resource_key = nil)
145
+ attrs, query_params = extract_query_params(body_data, resource_key)
146
+ # Only include path parameters from instance attributes, plus extracted query parameters
147
+ path_params = instance_attributes.select { |key, _| resource_path.named_params.values.include?(key) }
148
+ path_and_query_params = path_params.merge(query_params)
149
+ path = resource_path.apply_with_get_params(path_and_query_params)
88
150
 
89
- private
151
+ custom!(http_method, path, attrs)
152
+ end
153
+
154
+ # Class method version for enhanced custom! with query parameters
155
+ def self.custom_with_query_params!(http_method, body_data = {}, resource_key = nil)
156
+ attrs, query_params = extract_query_params(body_data, resource_key)
157
+ # Only include path parameters from body data, plus extracted query parameters
158
+ path_params = body_data.select { |key, _| resource_path.named_params.values.include?(key) }
159
+ path_and_query_params = path_params.merge(query_params)
160
+ path = resource_path.apply_with_get_params(path_and_query_params)
161
+
162
+ custom!(http_method, path, attrs)
163
+ end
90
164
 
91
165
  def self.build_connection
92
- Faraday.new(url: ChartMogul.api_base, headers: { 'User-Agent' => "chartmogul-ruby/#{ChartMogul::VERSION}" }) do |faraday|
166
+ Faraday.new(url: ChartMogul.api_base,
167
+ headers: { "User-Agent" => "chartmogul-ruby/#{ChartMogul::VERSION}" }) do |faraday|
93
168
  faraday.use Faraday::Response::RaiseError
94
- faraday.request :authorization, :basic, ChartMogul.api_key, ''
169
+ faraday.request :authorization, :basic, ChartMogul.api_key, ""
95
170
  faraday.request :retry, max: ChartMogul.max_retries, retry_statuses: RETRY_STATUSES,
96
- max_interval: MAX_INTERVAL, backoff_factor: BACKOFF_FACTOR,
97
- interval_randomness: INTERVAL_RANDOMNESS, interval: INTERVAL, exceptions: RETRY_EXCEPTIONS
171
+ max_interval: MAX_INTERVAL, backoff_factor: BACKOFF_FACTOR,
172
+ interval_randomness: INTERVAL_RANDOMNESS, interval: INTERVAL, exceptions: RETRY_EXCEPTIONS
98
173
  faraday.adapter Faraday::Adapter::NetHttp
99
174
  end
100
175
  end
@@ -1,21 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
3
+ require "forwardable"
4
4
 
5
5
  module ChartMogul
6
6
  class CustomerInvoices < APIResource
7
7
  extend Forwardable
8
8
  include Enumerable
9
9
 
10
- set_resource_name 'Invoices'
11
- set_resource_path '/v1/import/customers/:customer_uuid/invoices'
10
+ set_resource_name "Invoices"
11
+ set_resource_path "/v1/import/customers/:customer_uuid/invoices"
12
12
 
13
13
  writeable_attr :invoices, default: []
14
-
15
14
  writeable_attr :customer_uuid
15
+ writeable_query_param :handle_as_user_edit
16
16
 
17
17
  include API::Actions::All
18
- include API::Actions::Create
18
+ include API::Actions::Custom
19
19
  include Concerns::Pageable2
20
20
  include Concerns::PageableWithCursor
21
21
 
@@ -23,6 +23,29 @@ module ChartMogul
23
23
  map(&:serialize_for_write)
24
24
  end
25
25
 
26
+ def create!
27
+ # Only merge writeable_query_params to avoid overwriting serialized data
28
+ query_params_data = instance_attributes.select { |k, _| self.class.query_params.include?(k.to_sym) }
29
+ body_data = serialize_for_write.merge(query_params_data)
30
+ custom_with_query_params!(:post, body_data)
31
+ end
32
+
33
+ def self.create!(attributes = {})
34
+ resource = new(attributes)
35
+ # Only merge writeable_query_params to avoid overwriting serialized data
36
+ query_params_data = attributes.select { |k, _| query_params.include?(k.to_sym) }
37
+ body_data = resource.serialize_for_write.merge(query_params_data)
38
+ resource.custom_with_query_params!(:post, body_data)
39
+ end
40
+
41
+ def update!(attrs = {})
42
+ # Merge new attributes with existing, then only merge query params
43
+ updated_attrs = instance_attributes.merge(attrs)
44
+ query_params_data = updated_attrs.select { |k, _| self.class.query_params.include?(k.to_sym) }
45
+ body_data = serialize_for_write.merge(query_params_data)
46
+ custom_with_query_params!(:put, body_data)
47
+ end
48
+
26
49
  def self.all(customer_uuid, options = {})
27
50
  super(options.merge(customer_uuid: customer_uuid))
28
51
  end
@@ -32,7 +55,7 @@ module ChartMogul
32
55
  end
33
56
 
34
57
  def self.destroy_all!(data_source_uuid, customer_uuid)
35
- path = ChartMogul::ResourcePath.new('v1/data_sources/:data_source_uuid/customers/:customer_uuid/invoices')
58
+ path = ChartMogul::ResourcePath.new("v1/data_sources/:data_source_uuid/customers/:customer_uuid/invoices")
36
59
  handling_errors do
37
60
  connection.delete(path.apply(data_source_uuid: data_source_uuid, customer_uuid: customer_uuid))
38
61
  end
@@ -44,6 +67,7 @@ module ChartMogul
44
67
  private
45
68
 
46
69
  # TODO: replace with Entries concern?
70
+ # NOTE: called in the assign_all_attributes method
47
71
  def set_invoices(invoices_attributes)
48
72
  @invoices = invoices_attributes.map.with_index do |invoice_attributes, index|
49
73
  existing_invoice = invoices[index]
@@ -17,7 +17,6 @@ module ChartMogul
17
17
  writeable_attr :transaction_fees_currency
18
18
  writeable_attr :discount_description
19
19
  writeable_attr :event_order
20
-
21
20
  writeable_attr :plan_uuid
22
21
  writeable_attr :invoice_uuid
23
22
 
@@ -11,6 +11,7 @@ module ChartMogul
11
11
  readonly_attr :activity_mrr_movement
12
12
  readonly_attr :currency
13
13
  readonly_attr :subscription_external_id
14
+ readonly_attr :subscription_set_external_id
14
15
  readonly_attr :plan_external_id
15
16
  readonly_attr :customer_name
16
17
  readonly_attr :customer_uuid
@@ -23,6 +23,7 @@ module ChartMogul
23
23
  writeable_attr :amount_in_cents
24
24
  writeable_attr :tax_amount_in_cents
25
25
  writeable_attr :retracted_event_id
26
+ writeable_query_param :handle_as_user_edit
26
27
 
27
28
  include API::Actions::Custom
28
29
  include API::Actions::DestroyWithParams
@@ -32,7 +33,7 @@ module ChartMogul
32
33
  end
33
34
 
34
35
  def create!
35
- custom!(:post, resource_path.path, subscription_event: instance_attributes)
36
+ custom_with_query_params!(:post, { subscription_event: instance_attributes }, :subscription_event)
36
37
  end
37
38
 
38
39
  # This endpoint requires we send the attributes as:
@@ -40,19 +41,12 @@ module ChartMogul
40
41
  # So we do not include the API::Actions::Create module here and rather use a
41
42
  # variation of the method there to accommodate this difference in behaviour.
42
43
  def self.create!(attributes)
43
- resp = handling_errors do
44
- connection.post(resource_path.path) do |req|
45
- req.headers['Content-Type'] = 'application/json'
46
- req.body = JSON.dump({ subscription_event: attributes })
47
- end
48
- end
49
- json = ChartMogul::Utils::JSONParser.parse(resp.body, immutable_keys: immutable_keys)
50
-
51
- new_from_json(json)
44
+ custom_with_query_params!(:post, { subscription_event: attributes }, :subscription_event)
52
45
  end
53
46
 
54
47
  def update!(attrs)
55
- custom!(:patch, resource_path.path, subscription_event: attrs.merge(id: instance_attributes[:id]))
48
+ custom_with_query_params!(:patch, { subscription_event: attrs.merge(id: instance_attributes[:id]) },
49
+ :subscription_event)
56
50
  end
57
51
 
58
52
  def destroy!
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ChartMogul
4
- VERSION = '4.7.1'
4
+ VERSION = '4.9.0'
5
5
  end
data/lib/chartmogul.rb CHANGED
@@ -87,6 +87,7 @@ require 'chartmogul/enrichment/customer'
87
87
 
88
88
  module ChartMogul
89
89
  API_BASE = 'https://api.chartmogul.com'
90
+
90
91
  MAX_RETRIES = 20
91
92
  CONFIG_THREAD_KEY = 'chartmogul_ruby.config'
92
93
 
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chartmogul-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.7.1
4
+ version: 4.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Petr Kopac
8
+ autorequire:
8
9
  bindir: exe
9
10
  cert_chain: []
10
- date: 2025-07-25 00:00:00.000000000 Z
11
+ date: 2025-11-26 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: faraday
@@ -37,20 +38,6 @@ dependencies:
37
38
  - - "~>"
38
39
  - !ruby/object:Gem::Version
39
40
  version: '2.2'
40
- - !ruby/object:Gem::Dependency
41
- name: bundler
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '2'
47
- type: :development
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '2'
54
41
  - !ruby/object:Gem::Dependency
55
42
  name: cgi
56
43
  requirement: !ruby/object:Gem::Requirement
@@ -111,58 +98,58 @@ dependencies:
111
98
  name: rubocop
112
99
  requirement: !ruby/object:Gem::Requirement
113
100
  requirements:
114
- - - '='
101
+ - - ">="
115
102
  - !ruby/object:Gem::Version
116
- version: 1.56.3
103
+ version: '0'
117
104
  type: :development
118
105
  prerelease: false
119
106
  version_requirements: !ruby/object:Gem::Requirement
120
107
  requirements:
121
- - - '='
108
+ - - ">="
122
109
  - !ruby/object:Gem::Version
123
- version: 1.56.3
110
+ version: '0'
124
111
  - !ruby/object:Gem::Dependency
125
112
  name: rubocop-rspec
126
113
  requirement: !ruby/object:Gem::Requirement
127
114
  requirements:
128
- - - "~>"
115
+ - - ">="
129
116
  - !ruby/object:Gem::Version
130
- version: '2.9'
117
+ version: '0'
131
118
  type: :development
132
119
  prerelease: false
133
120
  version_requirements: !ruby/object:Gem::Requirement
134
121
  requirements:
135
- - - "~>"
122
+ - - ">="
136
123
  - !ruby/object:Gem::Version
137
- version: '2.9'
124
+ version: '0'
138
125
  - !ruby/object:Gem::Dependency
139
126
  name: rubocop-thread_safety
140
127
  requirement: !ruby/object:Gem::Requirement
141
128
  requirements:
142
- - - "~>"
129
+ - - ">="
143
130
  - !ruby/object:Gem::Version
144
- version: 0.5.1
131
+ version: '0'
145
132
  type: :development
146
133
  prerelease: false
147
134
  version_requirements: !ruby/object:Gem::Requirement
148
135
  requirements:
149
- - - "~>"
136
+ - - ">="
150
137
  - !ruby/object:Gem::Version
151
- version: 0.5.1
138
+ version: '0'
152
139
  - !ruby/object:Gem::Dependency
153
140
  name: simplecov
154
141
  requirement: !ruby/object:Gem::Requirement
155
142
  requirements:
156
- - - "~>"
143
+ - - ">="
157
144
  - !ruby/object:Gem::Version
158
- version: '0.21'
145
+ version: '0'
159
146
  type: :development
160
147
  prerelease: false
161
148
  version_requirements: !ruby/object:Gem::Requirement
162
149
  requirements:
163
- - - "~>"
150
+ - - ">="
164
151
  - !ruby/object:Gem::Version
165
- version: '0.21'
152
+ version: '0'
166
153
  - !ruby/object:Gem::Dependency
167
154
  name: vcr
168
155
  requirement: !ruby/object:Gem::Requirement
@@ -297,14 +284,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
297
284
  requirements:
298
285
  - - ">="
299
286
  - !ruby/object:Gem::Version
300
- version: '2.7'
287
+ version: '3.2'
301
288
  required_rubygems_version: !ruby/object:Gem::Requirement
302
289
  requirements:
303
290
  - - ">="
304
291
  - !ruby/object:Gem::Version
305
292
  version: '0'
306
293
  requirements: []
307
- rubygems_version: 3.6.2
294
+ rubygems_version: 3.4.10
295
+ signing_key:
308
296
  specification_version: 4
309
297
  summary: Chartmogul API Ruby Client
310
298
  test_files: []