Class: ChartMogul::APIResource

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/chartmogul/api_resource.rb

Constant Summary collapse

RETRY_STATUSES =
[429, *500..599].freeze
RETRY_EXCEPTIONS =
[
  "Faraday::ConnectionFailed",
  "Faraday::RetriableResponse"
].freeze
BACKOFF_FACTOR =
2
INTERVAL_RANDOMNESS =
0.5
INTERVAL =
1
MAX_INTERVAL =
60
THREAD_CONNECTION_KEY =
"chartmogul_ruby.api_resource.connection"

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Object

#allowed_for_write?, #assign_all_attributes, #assign_writeable_attributes, attributes, define_private_writer, define_reader, define_writer, #initialize, #instance_attributes, new_from_json, readonly_attr, #serialize_for_write, #serialized_value_for_attr, writeable_attr, writeable_attributes

Constructor Details

This class inherits a constructor from ChartMogul::Object

Class Attribute Details

.resource_nameObject (readonly)

Returns the value of attribute resource_name.



21
22
23
# File 'lib/chartmogul/api_resource.rb', line 21

def resource_name
  @resource_name
end

.resource_pathObject (readonly)

Returns the value of attribute resource_path.



21
22
23
# File 'lib/chartmogul/api_resource.rb', line 21

def resource_path
  @resource_path
end

.resource_root_keyObject (readonly)

Returns the value of attribute resource_root_key.



21
22
23
# File 'lib/chartmogul/api_resource.rb', line 21

def resource_root_key
  @resource_root_key
end

Class Method Details

.build_connectionObject



165
166
167
168
169
170
171
172
173
174
175
# File 'lib/chartmogul/api_resource.rb', line 165

def self.build_connection
  Faraday.new(url: ChartMogul.api_base,
    headers: { "User-Agent" => "chartmogul-ruby/#{ChartMogul::VERSION}" }) do |faraday|
    faraday.use Faraday::Response::RaiseError
    faraday.request :authorization, :basic, ChartMogul.api_key, ""
    faraday.request :retry, max: ChartMogul.max_retries, retry_statuses: RETRY_STATUSES,
      max_interval: MAX_INTERVAL, backoff_factor: BACKOFF_FACTOR,
      interval_randomness: INTERVAL_RANDOMNESS, interval: INTERVAL, exceptions: RETRY_EXCEPTIONS
    faraday.adapter Faraday::Adapter::NetHttp
  end
end

.connectionObject



72
73
74
# File 'lib/chartmogul/api_resource.rb', line 72

def self.connection
  Thread.current[THREAD_CONNECTION_KEY] ||= build_connection
end

.custom_with_query_params!(http_method, body_data = {}, resource_key = nil) ⇒ Object

Class method version for enhanced custom! with query parameters



155
156
157
158
159
160
161
162
163
# File 'lib/chartmogul/api_resource.rb', line 155

def self.custom_with_query_params!(http_method, body_data = {}, resource_key = nil)
  attrs, query_params = extract_query_params(body_data, resource_key)
  # Only include path parameters from body data, plus extracted query parameters
  path_params = body_data.select { |key, _| resource_path.named_params.values.include?(key) }
  path_and_query_params = path_params.merge(query_params)
  path = resource_path.apply_with_get_params(path_and_query_params)

  custom!(http_method, path, attrs)
end

.extract_query_params(attrs, resource_key = nil) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/chartmogul/api_resource.rb', line 32

def self.extract_query_params(attrs, resource_key = nil)
  remaining_attrs = attrs.dup
  query_params = {}

  self.query_params.each do |param|
    # If resource_key is specified, look in nested structure
    if resource_key && remaining_attrs[resource_key].is_a?(Hash) &&
       remaining_attrs[resource_key]&.key?(param) &&
       remaining_attrs[resource_key][param]
      query_params[param] = remaining_attrs[resource_key].delete(param)
    # Otherwise look at top level
    elsif remaining_attrs.key?(param) && remaining_attrs[param]
      query_params[param] = remaining_attrs.delete(param)
    end
  end

  [remaining_attrs, query_params]
end

.handle_other_error(exception) ⇒ Object



112
113
114
# File 'lib/chartmogul/api_resource.rb', line 112

def self.handle_other_error(exception)
  raise ChartMogul::ChartMogulError, exception.message
end

.handle_request_error(exception) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/chartmogul/api_resource.rb', line 84

def self.handle_request_error(exception)
  response = exception.response[:body]
  http_status = exception.response[:status]
  case http_status
  when 400
    message = "JSON schema validation hasn't passed."
    raise ChartMogul::SchemaInvalidError.new(message, http_status: 400, response: response)
  when 401
    message = "No valid API key provided"
    raise ChartMogul::UnauthorizedError.new(message, http_status: 401, response: response)
  when 403
    message = "The requested action is forbidden."
    raise ChartMogul::ForbiddenError.new(message, http_status: 403, response: response)
  when 404
    message = "The requested #{resource_name} could not be found."
    raise ChartMogul::NotFoundError.new(message, http_status: 404, response: response)
  when 422
    message = "The #{resource_name} could not be created or updated."
    raise ChartMogul::ResourceInvalidError.new(message, http_status: 422, response: response)
  when 500..504
    message = "ChartMogul API server response error"
    raise ChartMogul::ServerError.new(message, http_status: http_status, response: response)
  else
    message = "#{resource_name} request error has occurred."
    raise ChartMogul::ChartMogulError.new(message, http_status: http_status, response: response)
  end
end

.handling_errorsObject



76
77
78
79
80
81
82
# File 'lib/chartmogul/api_resource.rb', line 76

def self.handling_errors
  yield
rescue Faraday::ClientError, Faraday::ServerError => e
  e.response ? handle_request_error(e) : handle_other_error(e)
rescue StandardError => e
  handle_other_error(e)
end

.immutable_keysObject



63
64
65
# File 'lib/chartmogul/api_resource.rb', line 63

def self.immutable_keys
  @immutable_keys ||= []
end

.query_paramsObject



23
24
25
# File 'lib/chartmogul/api_resource.rb', line 23

def self.query_params
  @query_params ||= Set.new
end

.set_immutable_keys(array) ⇒ Object

When set with keys, nested hash keys of these immutable keys won’t be converted to snake case



68
69
70
# File 'lib/chartmogul/api_resource.rb', line 68

def self.set_immutable_keys(array)
  @immutable_keys = array
end

.set_resource_name(name) ⇒ Object



55
56
57
# File 'lib/chartmogul/api_resource.rb', line 55

def self.set_resource_name(name)
  @resource_name = name
end

.set_resource_path(path) ⇒ Object



51
52
53
# File 'lib/chartmogul/api_resource.rb', line 51

def self.set_resource_path(path)
  @resource_path = ChartMogul::ResourcePath.new(path)
end

.set_resource_root_key(root_key) ⇒ Object



59
60
61
# File 'lib/chartmogul/api_resource.rb', line 59

def self.set_resource_root_key(root_key)
  @resource_root_key = root_key
end

.writeable_query_param(attribute, options = {}) ⇒ Object



27
28
29
30
# File 'lib/chartmogul/api_resource.rb', line 27

def self.writeable_query_param(attribute, options = {})
  query_params << attribute.to_sym
  writeable_attr(attribute, options)
end

Instance Method Details

#custom_with_query_params!(http_method, body_data = {}, resource_key = nil) ⇒ Object

Enhanced custom! that automatically handles query parameters



144
145
146
147
148
149
150
151
152
# File 'lib/chartmogul/api_resource.rb', line 144

def custom_with_query_params!(http_method, body_data = {}, resource_key = nil)
  attrs, query_params = extract_query_params(body_data, resource_key)
  # Only include path parameters from instance attributes, plus extracted query parameters
  path_params = instance_attributes.select { |key, _| resource_path.named_params.values.include?(key) }
  path_and_query_params = path_params.merge(query_params)
  path = resource_path.apply_with_get_params(path_and_query_params)

  custom!(http_method, path, attrs)
end

#extract_query_params(attrs, resource_key = nil) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/chartmogul/api_resource.rb', line 118

def extract_query_params(attrs, resource_key = nil)
  remaining_attrs = attrs.dup
  query_params = {}

  self.class.query_params.each do |param|
    # If resource_key is specified, look in nested structure
    if resource_key && remaining_attrs[resource_key].is_a?(Hash) &&
       remaining_attrs[resource_key]&.key?(param) &&
       remaining_attrs[resource_key][param]
      query_params[param] = remaining_attrs[resource_key].delete(param)
    # Otherwise look at top level
    elsif remaining_attrs.key?(param) && remaining_attrs[param]
      query_params[param] = remaining_attrs.delete(param)
    end
  end

  [remaining_attrs, query_params]
end

#path_with_query_params(attrs) ⇒ Object

Generate path with query parameters applied



138
139
140
141
# File 'lib/chartmogul/api_resource.rb', line 138

def path_with_query_params(attrs)
  _, query_params = extract_query_params(attrs)
  query_params.empty? ? resource_path.path : resource_path.apply_with_get_params(query_params)
end