tribune_recurly_api 0.4.3 → 1.0.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: 0b9eafd5553b067ea352e854a202c773f0565c728b0fd89c1b8775702f36ee29
4
- data.tar.gz: 73269fbd07f54d384a52a04bdc3602c1162386cf942763e2becd16b336a728a9
3
+ metadata.gz: 90bc9314bc9a69973358b2865c2f75c5d4ce83f17393304deec39e8dae4dc2d4
4
+ data.tar.gz: 44ba860e9260009fb54fc277fc7418b5da1325d4d304dff30f7fe247b544450c
5
5
  SHA512:
6
- metadata.gz: 0765af740d352b35433b5fe4b0ab2b4b0c19428b776c4f69c42dac223f7290d839dc1c4a0345f4cbb53d057d306250ed94708f6743df027d6f16fc227b95015f
7
- data.tar.gz: cb56612cea6cd6584ecbaae4c31d31d3935167dc7b31c71abd1be887b6fde12b2cbc26b664ad4ecb7ba923b575c2a09bf00915ef1ba28eaf012b270e7bb0baf2
6
+ metadata.gz: 8a526bdd23c232b57019d4f5bd8356781cbfd696efd32fef9baefabda89d3b469261dc8ad1e3a313548f269d517e29f8f81a95e5a30a53fd285bfdae20f3adba
7
+ data.tar.gz: 5bfccbbc08e4fdb9edf9a972dade04955d92094af277546eff4698a55e67220e8fa1e2e0dbe8b60e05b4677175f80818c225284da9da8726d532d7c064debcb8
data/lib/recurly_api.rb CHANGED
@@ -1,22 +1,8 @@
1
- # frozen_string_literal: true
2
-
3
- require 'recurly_api/version'
4
- require 'recurly_api/client'
5
- # some info goes here..
6
- module RecurlyApi
7
- class Error < StandardError; end
8
-
9
- # RecurlyApi.logger.debug("I'm a debug log")
10
- # logger.info("I'm an info log")
11
- # logger.warn("I'm a warn log")
12
- # logger.error("I'm an error log: error message")
13
- # logger.fatal("I'm a fatal log")
14
- def self.logger
15
- # @@logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
16
- @@logger ||= Logger.new(STDOUT)
17
- end
18
-
19
- def self.logger=(logger)
20
- @@logger = logger
21
- end
22
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'recurly_api/version'
4
+ require 'recurly_api/client'
5
+ # some info goes here..
6
+ module RecurlyApi
7
+ class Error < StandardError; end
8
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Perform caching for Recurly Responses( only for GET requests)
4
+ module RecurlyApi
5
+ # Don't perform caching for non-rails projects
6
+ module Caching
7
+ # accessor for getter and setter methods
8
+ attr_accessor :ignore_caching, # skip/bypass caching true|false
9
+ :cache_expires_in,
10
+ :cache_key # key name to read/write cache, # default value is caller method_name
11
+
12
+ # default settings for caching, every GET request is cached by default with default expiry_time 5.seconds
13
+ CACHE_SETTINGS = { bypass: false,
14
+ expiry_time_in_secs: 60 }.freeze
15
+
16
+ # Usage examples to perform caching in RecurlyApi:Client
17
+ # cache.write('key', 'val', expires_in: 30)
18
+ # cache.fetch('key')
19
+ def cache
20
+ @cache ||= defined?(Rails) ? Rails.cache : nil
21
+ end
22
+
23
+ # for non-rails applications do not perform caching on Recurly Response's
24
+ # perform caching only if it is enabled in Rails application
25
+ def caching_enabled?
26
+ !cache.nil? && Rails.application.config.action_controller.perform_caching
27
+ end
28
+
29
+ # tribune_recurly_api GEM related cache saved as 'tribune_recurly_api.<cache_key>' in cache_store
30
+ def cache_prefix_name
31
+ 'tribune_recurly_api.'
32
+ end
33
+
34
+ def cache_key_with_prefix
35
+ "#{cache_prefix_name}#{cache_key}"
36
+ end
37
+
38
+ def ignore_caching?
39
+ @ignore_caching
40
+ end
41
+ alias bypass_caching? ignore_caching? # both are same
42
+
43
+ def write_final_resp_to_cache
44
+ cache.write(cache_key_with_prefix, final_response, expires_in: cache_expires_in)
45
+ end
46
+
47
+ def fetch_response_from_cache
48
+ # cache.exist?(cache_key_with_prefix) # check if cache already exists or not for requested API call
49
+ cache.fetch(cache_key_with_prefix) # returns nil if cache not exists for requested API call.
50
+ end
51
+
52
+ # TODO: remove below method once integration testing is done
53
+ def check_logs_for_caching_info
54
+ logger.info("#{logger_heading}: ----> caching_enabled?: #{caching_enabled?}")
55
+ # logger.info("#{logger_heading}: ----> ignore_caching?: #{ignore_caching?}")
56
+ logger.info("#{logger_heading}: ----> bypass_caching?: #{bypass_caching?}")
57
+ logger.info("#{logger_heading}: ----> http_method: #{http_method} -- #{http_method.class}")
58
+ logger.info("#{logger_heading}: ----> cache_expires_in: #{cache_expires_in}")
59
+ logger.info("#{logger_heading}: ----> cache_key: #{cache_key} -- #{cache_key.class}")
60
+ # logger.info("#{logger_heading}: ----> final response: #{final_response || fetch_response_from_cache}")
61
+ logger.info("#{logger_heading}: ----> ratelimit_retries: #{ratelimit_retries}")
62
+ end
63
+ end
64
+ end
@@ -1,142 +1,183 @@
1
- # frozen_string_literal: true
2
- #
3
- require 'rest-client'
4
- require 'json'
5
- module RecurlyApi
6
- # Wrapper class to connect Recurly API end-points
7
- class Client
8
- # Requesting API end-points
9
- require_relative './client/accounts'
10
- require_relative './client/plans'
11
- require_relative './client/subscriptions'
12
- require_relative './client/other_requests.rb'
13
-
14
- API_DEFAULTS = { base_host: 'v3.recurly.com',
15
- api_version: 'v2021-02-25' }.freeze
16
- # request for The number of records to return per page
17
- RECORDS_LIMIT = 20
18
- RATE_LIMIT_MAX_RETRIES = 3
19
-
20
- attr_accessor :site_id, :authorization_key, :base_host,
21
- :api_version, :api_endpoint, :ratelimit_retries
22
-
23
- # Initialize a client.
24
- # KeyWord args are optional while initializing and defaults to the values present in recurly_config.yml,
25
- # Ex: If you want to use custom 'api_version' instead predefined, initialize client as below,
26
- # rc = RecurlyApi::Client.new(api_version: 'v2019-10-10')
27
- # @param authorization_key [String] Required, Recurly API auth key.
28
- # @param site_id [String] Required, ex: 'subdomain-tribune' for https://tribune.recurly.com
29
- # @param base_host [String] Optional, default: v3.recurly.com
30
- # @param api_version [String] Optional, Recurly api_version ex: 'v2021-02-25' | 'v2019-10-10'
31
- # @param ratelimit_retries [Integer] Optional, retry limit for rate limit exceeds, default: 3
32
- def initialize(authorization_key: nil,
33
- site_id: nil,
34
- base_host: nil,
35
- api_version: nil,
36
- ratelimit_retries: RATE_LIMIT_MAX_RETRIES)
37
- @authorization_key = authorization_key
38
- @site_id = site_id
39
- raise ArgumentError, "'authorization_key' must be set to a non-nil value" if @authorization_key.nil?
40
- raise ArgumentError, "'site_id' must be set to a non-nil value" if @site_id.nil?
41
-
42
- @base_host = base_host || API_DEFAULTS[:base_host]
43
- @api_version = api_version || API_DEFAULTS[:api_version]
44
- @api_endpoint = "https://#{@base_host}/sites/#{@site_id}"
45
- @ratelimit_retries = ratelimit_retries
46
- end
47
-
48
- # request Recurly V3 API (v2021-02-25)(https://developers.recurly.com/api/v2021-02-25/)
49
- # @param path [String] path for API call -> ex: 'plans/{plan_id}', 'accounts/{account_id}'.
50
- # @param http_method [Symbol] request method -> ex: get | post | put | delete.
51
- # @param params [Hash] query parameters -> ex: { limit: 20, order: :asc etc }
52
- # @param payload [Hash] request body parameters( for post, put etc..)
53
- # @param add_headers [Hash] additional headers if any ex: User-Agent etc..
54
- # RestClient retry { https://blog.appsignal.com/2018/05/16/ensure-retry-and-reraise-exceptions-in-ruby.html }
55
- def request_api(path:, http_method: :get, params: {}, payload: {}, add_headers: {})
56
- raise ArgumentError, "'request path' must be set to a non-nil value" if path.nil?
57
-
58
- max_retries = ratelimit_retries # max count to retry if rate limit requests are exceeded.
59
- retry_count = 0
60
- delay = 1 # in seconds
61
- end_point = "#{api_endpoint}/#{path}"
62
- headers = ensure_request_headers(add_headers).merge!(params: params)
63
- resp = RestClient::Request.execute(method: http_method,
64
- url: end_point,
65
- payload: payload.empty? ? payload : payload.to_json,
66
- headers: headers)
67
- rescue RestClient::ExceptionWithResponse => e
68
- e.response
69
- rescue RestClient::Exception => e
70
- # Timed out reading data from server
71
- e.response
72
- rescue StandardError => e
73
- raise e.message
74
- else
75
- begin
76
- raise 'API Rate limit exceeded' if rate_limit_exceeded?(resp)
77
- rescue StandardError => _e
78
- RecurlyApi.logger.error "API Rate limit exceeded retrying again. Retries left: #{max_retries - retry_count}"
79
- sleep delay += retry_count
80
- retry_count += 1
81
- retry if retry_count < max_retries
82
- end
83
- resp
84
- end
85
-
86
- def ensure_request_headers(add_headers)
87
- accept_header = "application/vnd.recurly.#{api_version}+json"
88
- { "Authorization": authorization_key,
89
- "Content-Type": 'application/json',
90
- accept: accept_header }.merge!(add_headers)
91
- end
92
-
93
- def handle_response!(resp, payload_name: :data, single_node: false)
94
- if rate_limit_exceeded?(resp)
95
- handle_rate_limit_exceed(resp)
96
- else
97
- json_body = JSON.parse(resp.body)
98
- if json_body['error']
99
- handle_error_response(resp.code, json_body)
100
- else
101
- { success: true, status: resp.code,
102
- "#{payload_name}": single_node ? json_body : json_body['data'] }
103
- end
104
- end
105
- end
106
-
107
- def handle_error_response(code, json_body)
108
- { success: false, status: code,
109
- error: json_body['error']['type'],
110
- message: json_body['error']['message'] }
111
- end
112
-
113
- # Recurly rate limiting
114
- # { https://developers.recurly.com/api/v2019-10-10/#section/Getting-Started/Limits }
115
- # The rate limit is calculated over a sliding 5 minute window.
116
- # This means a production site could make 4,000 requests within one minute and not hit the rate limit
117
- # so long as the site made less than 1,000 requests during the prior 4 minute
118
- # --------------
119
- # Response Headers that returned related to rate limiting by Recurly.
120
- # x_ratelimit_limit: The maximum number of requests available in the current time frame(5-minite window)
121
- # x_ratelimit_remaining: the number of requests left for the 5-minute window
122
- # x_ratelimit_reset: the remaining window before the rate limit resets, in UNIX Epoch seconds
123
-
124
- # Check if rate limit exceeded or not using resp status code OR x_ratelimit_remaining header
125
- def rate_limit_exceeded?(resp)
126
- ratelimit_remaining = resp.headers[:x_ratelimit_remaining]
127
- resp.code.eql?(429) || (ratelimit_remaining && ratelimit_remaining.to_i < 1)
128
- end
129
-
130
- def handle_rate_limit_exceed(resp)
131
- resp_headers = resp.headers
132
- rate_limit_info = { limit: resp_headers[:x_ratelimit_limit],
133
- remaining: resp_headers[:x_ratelimit_remaining],
134
- reset_time_seconds: resp_headers[:x_ratelimit_reset],
135
- reset_time: DateTime.strptime(resp_headers[:x_ratelimit_reset], '%s') }
136
- { success: false, status: 429,
137
- error: 'Recurly API Rate limit exceeded',
138
- message: 'Too Many Requests, See rate-limiting for more info',
139
- rate_limit_info: rate_limit_info }
140
- end
141
- end
142
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'rest-client'
4
+ require 'json'
5
+ require 'recurly_api/logging'
6
+ require 'recurly_api/exception_handler'
7
+ require 'recurly_api/rate_limiting'
8
+ require 'recurly_api/caching'
9
+ module RecurlyApi
10
+ # Wrapper class to connect Recurly API end-points
11
+ # rubocop:disable Metrics/ClassLength
12
+ class Client
13
+ include Logging
14
+ include ExceptionHandler
15
+ include RateLimiting
16
+ include Caching
17
+
18
+ # Requesting API end-points
19
+ require_relative './client/accounts'
20
+ require_relative './client/plans'
21
+ require_relative './client/subscriptions'
22
+ require_relative './client/other_requests.rb'
23
+ API_DEFAULTS = { base_host: 'v3.recurly.com',
24
+ api_version: 'v2021-02-25' }.freeze
25
+ # request for The number of records to return per page
26
+ RECORDS_LIMIT = 20
27
+
28
+ # Initialize method attributes
29
+ attr_accessor :site_id, :authorization_key, :base_host,
30
+ :api_version, :api_endpoint
31
+ # request attributes for request_api method
32
+ attr_accessor :path_name, :http_method, :query_params,
33
+ :payload, :additional_headers, :payload_name,
34
+ :cache_key_name, :optional_params
35
+ # response attributes
36
+ attr_accessor :recurly_response, :final_response
37
+
38
+
39
+ # Initialize a client.
40
+ # KeyWord args are optional while initializing and defaults to the values present in recurly_config.yml(Rails only),
41
+ # Ex: If you want to use custom 'api_version' instead predefined, initialize client as below,
42
+ # rc = RecurlyApi::Client.new(api_version: 'v2019-10-10')
43
+ # @param authorization_key [String] Required, Recurly API auth key.
44
+ # @param site_id [String] Required, ex: 'subdomain-tribune' for https://tribune.recurly.com
45
+ # @param base_host [String] Optional, default: v3.recurly.com
46
+ # @param api_version [String] Optional, Recurly api_version ex: 'v2021-02-25' | 'v2019-10-10'
47
+ # @param ratelimit_retries [Integer] Optional, retry limit for rate limit exceeds, default: 3
48
+ def initialize(authorization_key:,
49
+ site_id:,
50
+ base_host: nil,
51
+ api_version: nil,
52
+ ratelimit_retries: RATE_LIMIT_MAX_RETRIES)
53
+ @authorization_key = authorization_key
54
+ @site_id = site_id
55
+ raise ArgumentError, "'authorization_key' must be set to a non-nil value" if @authorization_key.nil?
56
+ raise ArgumentError, "'site_id' must be set to a non-nil value" if @site_id.nil?
57
+
58
+ @base_host = base_host || API_DEFAULTS[:base_host]
59
+ @api_version = api_version || API_DEFAULTS[:api_version]
60
+ @api_endpoint = "https://#{@base_host}/sites/#{@site_id}"
61
+ @ratelimit_retries = ratelimit_retries
62
+ @cache_expires_in = CACHE_SETTINGS[:expiry_time_in_secs]
63
+ @ignore_caching = CACHE_SETTINGS[:bypass]
64
+ end
65
+
66
+ # TODO: remove unnecessary logs once integration testing completed
67
+ # request Recurly V3 API (v2021-02-25)(https://developers.recurly.com/api/v2021-02-25/)
68
+ # @param path_name [String] Required, path for API call -> ex: 'plans/{plan_id}', 'accounts/{account_id}'.
69
+ # #@param http_method [Symbol] Optional, default: :get -> ex: get | post | put | delete.
70
+ # @param query_params [Hash] Optional, Recurly query string parameters:
71
+ # i. :ids [String] Filter results by their IDs. Up to 200 IDs can be passed at once using
72
+ # commas as separators, e.g. <ids=h1at4d57xlmy,gyqgg0d3v9n1,jrsm5b4yefg6>.
73
+ # ii. :limit [Integer] Limit number of records 1-200.
74
+ # iii. :order [String] Sort order.
75
+ # iv: :sort [String] Sort field. You *really* only want to sort by <updated_at> in ascending
76
+ # @param additional_headers [Hash] Optional, additional headers if any ex: User-Agent etc..
77
+ # @param payload [Hash] Optional, request body parameters( for post, put etc..)
78
+ # @param optional_params [Hash] Optional, or any of below ( ref full usage example)
79
+ # :payload_name [Symbol] Optional, defaults to ':data'
80
+ # :bypass_caching [Boolean] Optional, default value is false
81
+ # :cache_expiry_in_secs [Integer] Optional, default value is 60 seconds
82
+
83
+ # Ful Usage Example: Below is the example for 'request_api' call that includes all options
84
+ # res = request_api(path_name: 'plans', # required
85
+ # http_method: :get, # optional, :post|put|patch|delete
86
+ # query_params: recurly_query_params, # optional
87
+ # payload: {}, # optional, request body params
88
+ # additional_headers: { 'User-Agent': 'abc', ..}, # optional
89
+ # cache_key_name: 'key123', # optional, defaults to method_name
90
+ # bypass_caching: true, # optionals, default false
91
+ # payload_name: 'list_all_plans', # optional defaults to caller method_name
92
+ # cache_expiry_in_secs: 180 # optional default 60
93
+ # )
94
+ # RestClient retry { https://blog.appsignal.com/2018/05/16/ensure-retry-and-reraise-exceptions-in-ruby.html }
95
+ # rubocop:disable Metrics/ParameterLists
96
+ # rubocop:disable Metrics/AbcSize
97
+ # rubocop:disable Metrics/PerceivedComplexity
98
+ def request_api(path_name:, http_method: :get, query_params: {},
99
+ payload: {}, additional_headers: {},
100
+ cache_key_name: nil, **optional_params)
101
+ raise ArgumentError, "'request path' must be set to a non-nil value" if path_name.nil?
102
+
103
+ self.path_name = path_name # set the HTTP Verb (caching is only done for HTTP GET requests)
104
+ self.http_method = http_method # set the HTTP Verb (caching is only done for HTTP GET requests)
105
+ self.query_params = query_params
106
+ self.payload = payload
107
+ self.additional_headers = additional_headers
108
+ self.cache_key_name = cache_key_name
109
+ self.optional_params = optional_params
110
+ # optional params ===>
111
+ self.payload_name = optional_params[:payload_name] || 'payload'
112
+ self.ignore_caching = optional_params[:bypass_caching] || ignore_caching
113
+ self.cache_expires_in = optional_params[:cache_expiry_in_secs] || cache_expires_in
114
+ # caller_method_name = caller_locations.first.label # Ruby 2.0 +
115
+ caller_method_name = caller(1..1).first[/`(.*)'/, 1] # Prior Ruby 2.0
116
+ self.cache_key = cache_key_name || caller_method_name # fallbacks to caller method if cache_key_name not present
117
+ # TODO: remove below method once integration testing is completed, also remove unnecessary logs
118
+ check_logs_for_caching_info
119
+ # Cashing related stuff... (caching is performed on response only on :get requests when it is enabled)
120
+ # by default Cache bypassed for non rails applications
121
+ if caching_enabled? && !bypass_caching? && http_method.eql?(:get)
122
+ cached_response = fetch_response_from_cache
123
+ # Do NOT use a falsy check because we need to distinguish 'false' from 'nil'
124
+ if cached_response.nil?
125
+ fetch_response_from_recurly(cache_recurly_resp: true)
126
+ else
127
+ logger.info("#{logger_cache_heading}: response returned from cache store and key is: #{cache_key_with_prefix}")
128
+ cached_response
129
+ end
130
+ else
131
+ logger.warn("#{logger_cache_heading}: caching of Recurly Response completely bypassed")
132
+ fetch_response_from_recurly
133
+ end
134
+ end
135
+
136
+ def fetch_response_from_recurly(cache_recurly_resp: false)
137
+ # rescue/raise all your all exceptions in ExceptionHandler's method
138
+ recurly_api_exception_handler do
139
+ end_point = "#{api_endpoint}/#{path_name}"
140
+ headers = ensure_request_headers.merge!(params: query_params)
141
+ self.recurly_response = RestClient::Request.execute(method: http_method,
142
+ url: end_point,
143
+ payload: payload.empty? ? nil : payload.to_json,
144
+ headers: headers)
145
+ self.final_response = handle_recurly_response!
146
+ retry_on_rate_limit_exceed
147
+ # don't cache rate_limit_exceed_response
148
+ if cache_recurly_resp && !rate_limit_exceeded?
149
+ write_final_resp_to_cache
150
+ logger.info("#{logger_cache_heading}: Recurly Response saved to cache_store with Key: #{cache_key_with_prefix}")
151
+ end
152
+ final_response
153
+ end
154
+ end
155
+
156
+ def ensure_request_headers
157
+ accept_header = "application/vnd.recurly.#{api_version}+json"
158
+ { "Authorization": authorization_key,
159
+ "Content-Type": 'application/json',
160
+ accept: accept_header }.merge!(additional_headers)
161
+ end
162
+
163
+ def handle_recurly_response!
164
+ if rate_limit_exceeded?
165
+ rate_limit_exceed_response
166
+ else
167
+ json_body = JSON.parse(recurly_response.body)
168
+ if json_body['error']
169
+ handle_error_response(recurly_response.code, json_body)
170
+ else
171
+ { success: true, status_code: recurly_response.code,
172
+ "#{payload_name}": json_body }
173
+ end
174
+ end
175
+ end
176
+
177
+ def handle_error_response(code, json_body)
178
+ { success: false, status_code: code,
179
+ error: json_body['error']['type'],
180
+ message: json_body['error']['message'] }
181
+ end
182
+ end
183
+ end
@@ -1,25 +1,23 @@
1
- # frozen_string_literal: true
2
-
3
- module RecurlyApi
4
- # API requests to fetch Accounts related data
5
- class Client
6
- # Fetch Account Info--
7
- # { https://developers.recurly.com/api/v2021-02-25/index.html#operation/get_account }
8
- # @param ssor_id [String] Required, Account ID(SsorID) or code.
9
- # For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-bob+.
10
- def account_info(ssor_id:)
11
- resp = request_api(path: "accounts/#{ssor_id}")
12
- handle_response!(resp, payload_name: :account_info, single_node: true)
13
- end
14
-
15
- # Deactivate account ---
16
- # { https://developers.recurly.com/api/v2021-02-25/index.html#operation/deactivate_account }
17
- # @param ssor_id [String] Required, Account ID(SsorID) or code.
18
- # For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-bob+.
19
- def deactivate_account(ssor_id:)
20
- path = "accounts/#{ssor_id}"
21
- resp = request_api(path: path, http_method: :delete)
22
- handle_response!(resp, payload_name: :account_info, single_node: true)
23
- end
24
- end
25
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RecurlyApi
4
+ # API requests to fetch Accounts related data
5
+ class Client
6
+ # Fetch Account Info--
7
+ # { https://developers.recurly.com/api/v2021-02-25/index.html#operation/get_account }
8
+ # @param ssor_id [String] Required, Account ID(SsorID) or code.
9
+ # For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-bob+.
10
+ def account_info(ssor_id:, **optional_params)
11
+ request_api(path_name: "accounts/#{ssor_id}", **optional_params) # cache_key_name: cache_key_name
12
+ end
13
+
14
+ # Deactivate account ---
15
+ # { https://developers.recurly.com/api/v2021-02-25/index.html#operation/deactivate_account }
16
+ # @param ssor_id [String] Required, Account ID(SsorID) or code.
17
+ # For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-bob+.
18
+ def deactivate_account(ssor_id:)
19
+ path = "accounts/#{ssor_id}"
20
+ request_api(path_name: path, http_method: :delete)
21
+ end
22
+ end
23
+ end
@@ -1,17 +1,16 @@
1
- # frozen_string_literal: true
2
-
3
- module RecurlyApi
4
- # This class fetches the data of Recurly API end-points
5
- class Client
6
- # Show the coupon redemptions for an account --
7
- # { https://developers.recurly.com/api/v2021-02-25/index.html#tag/coupon_redemption }
8
- # @param ssor_id [String] Recurly Account ID(SsorID) or code.
9
- # For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-bob+.
10
- def coupon_redemptions(ssor_id:)
11
- resp = request_api(path: "accounts/#{ssor_id}/coupon_redemptions")
12
- handle_response!(resp, payload_name: :coupon_redemptions, single_node: true)
13
- end
14
-
15
- # Other request any.....
16
- end
17
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RecurlyApi
4
+ # This class fetches the data of Recurly API end-points
5
+ class Client
6
+ # Show the coupon redemptions for an account --
7
+ # { https://developers.recurly.com/api/v2021-02-25/index.html#tag/coupon_redemption }
8
+ # @param ssor_id [String] Recurly Account ID(SsorID) or code.
9
+ # For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-bob+.
10
+ def coupon_redemptions(ssor_id:, **optional_params)
11
+ request_api(path_name: "accounts/#{ssor_id}/coupon_redemptions", **optional_params)
12
+ end
13
+
14
+ # Other request any.....
15
+ end
16
+ end
@@ -1,32 +1,43 @@
1
- # frozen_string_literal: true
2
-
3
- module RecurlyApi
4
- # API requests to fetch Plans related data
5
- class Client
6
- # List a site's plans
7
- # { https://developers.recurly.com/api/v2021-02-25/index.html#operation/list_plans }
8
- # @param params [Hash] Optional query string parameters:
9
- # :ids [String] Filter results by their IDs. Up to 200 IDs can be passed at once using
10
- # commas as separators, e.g. +ids=h1at4d57xlmy,gyqgg0d3v9n1,jrsm5b4yefg6+.
11
- # :limit [Integer] Limit number of records 1-200.
12
- # :order [String] Sort order.
13
- # :sort [String] Sort field. You *really* only want to sort by +updated_at+ in ascending
14
- # ex:
15
- # params = { limit: 2, order: :asc, .....}
16
- # ex: client.list_plans(params)
17
- def list_plans(**params)
18
- resp = request_api(path: 'plans', params: params)
19
- # handle_response(resp, payload_name: :plans)
20
- handle_response!(resp)
21
- end
22
-
23
- # Fetch Plan Info--
24
- # { https://developers.recurly.com/api/v2021-02-25/index.html#operation/get_plan}
25
- # @param plan_id [String] Plan ID or code.
26
- # For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-gold+.
27
- def plan_info(plan_id:)
28
- resp = request_api(path: "plans/#{plan_id}")
29
- handle_response!(resp, payload_name: :plan_info, single_node: true)
30
- end
31
- end
32
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RecurlyApi
4
+ # API requests to fetch Plans related data
5
+ class Client
6
+ # List a site's plans
7
+ # { https://developers.recurly.com/api/v2021-02-25/index.html#operation/list_plans }
8
+ # @param recurly_query_params [Hash] Optional, Recurly query string parameters:
9
+ # i. :ids [String] Filter results by their IDs. Up to 200 IDs can be passed at once using
10
+ # commas as separators, e.g. <ids=h1at4d57xlmy,gyqgg0d3v9n1,jrsm5b4yefg6>.
11
+ # ii. :limit [Integer] Limit number of records 1-200.
12
+ # iii. :order [String] Sort order.
13
+ # iv: :sort [String] Sort field. You *really* only want to sort by <updated_at> in ascending
14
+ # @param additional_headers [Hash] Optional, additional headers if any ex: User-Agent etc..
15
+ # @param payload [Hash] Optional, request body parameters( for post, put etc..)
16
+ # @param optional_params [Hash] Optional, or any of below ( ref full usage example)
17
+ # :payload_name [Symbol] Optional, defaults to caller method_name
18
+ # :bypass_caching [Boolean] Optional, default value is false
19
+ # :cache_expiry_in_secs [Integer] Optional, default value is 60 seconds
20
+
21
+ # Examples:
22
+ # query_params = { limit: 2, order: :asc, ids: ['abc', 'def'..etc], :sort: <value>}
23
+ # client.list_plans(recurly_query_params: query_params, bypass_cache: true)
24
+ # client.list_plans(recurly_query_params: query_params, cache_expires_in: 120, payload_name: :fetch_plans)
25
+ # client.list_plans(recurly_query_params: query_params)
26
+
27
+ def list_plans(recurly_query_params: {}, **optional_params)
28
+ # payload_name = optional_params[:payload_name] # if not present default value is :payload
29
+ cache_key_name = 'list_all_plans' # optional by default cache_key is caller method_name in receiver i.e. list_plans here
30
+ request_api(path_name: 'plans', query_params: recurly_query_params,
31
+ cache_key_name: cache_key_name, **optional_params)
32
+ end
33
+
34
+ # Fetch Plan Info--
35
+ # { https://developers.recurly.com/api/v2021-02-25/index.html#operation/get_plan}
36
+ # @param plan_id [String] Required, Plan ID or code.
37
+ # - For ID no prefix is used e.g. <e28zov4fw0v2>. For code use prefix <code->, ex. <code-gold>.
38
+ # @param optional_params [Hash] Optional, same as above
39
+ def plan_info(plan_id:, **optional_params)
40
+ request_api(path_name: "plans/#{plan_id}", **optional_params) # cache_key_name: cache_key_name
41
+ end
42
+ end
43
+ end
@@ -1,98 +1,94 @@
1
- # frozen_string_literal: true
2
-
3
- module RecurlyApi
4
- # API requests to fetch Subscriptions related data
5
- class Client
6
- # Fetch Account Subscriptions ---
7
- # { https://developers.recurly.com/api/v2019-10-10/index.html#operation/list_account_subscriptions }
8
- # @param ssor_id [String] Account ID(SsorID) or code.
9
- # For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-gold+.
10
- # For SsorID no prefix is required
11
- # @param params [Hash] Optional query string parameters
12
- def account_subscriptions(ssor_id:, **params)
13
- path = "accounts/#{ssor_id}/subscriptions"
14
- resp = request_api(path: path, params: params)
15
- handle_response!(resp)
16
- end
17
-
18
- # Checking if user already has subscription OR
19
- # Checking if user has a subscription for particular plan by its code
20
- # ---------------
21
- # @param ssor_id [String] required, Recurly Account Code(i.e SsorID)
22
- # @prams plan_code [String] optional,
23
- # - if present then checks if user has subscription for that particular plan by its code
24
- # Usage ex:
25
- # client.check_user_subscription(ssor_id: '4900-0272-6875', plan_code: '000150d03d')
26
- # response => { :success=>true, :status=>200, :has_subscription=>true }
27
- def check_user_subscription(ssor_id:, plan_code: nil)
28
- resp = account_subscriptions(ssor_id: "code-#{ssor_id}")
29
- if resp[:success]
30
- has_subscription = false
31
- subs = resp[:data]
32
- has_subscription = true if subs.any?
33
- if plan_code
34
- sub = subs.detect { |s| s['plan']['code'].eql?(plan_code) }
35
- has_subscription = sub ? true : false
36
- end
37
- { success: true, status: 200, has_subscription: has_subscription }
38
- else
39
- resp
40
- end
41
- end
42
-
43
- # List a site's subscriptions
44
- # { https://developers.recurly.com/api/v2021-02-25/#operation/list_account_subscriptions }
45
- # @param params [Hash] Optional query string parameters:
46
- # :ids [String] Filter results by their IDs. Up to 200 IDs can be passed at once using
47
- # commas as separators, e.g. +ids=h1at4d57xlmy,gyqgg0d3v9n1,jrsm5b4yefg6+.
48
- # :limit [Integer] Limit number of records 1-200.
49
- # :order [String] Sort order.
50
- # :sort [String] Sort field. You *really* only want to sort by +updated_at+ in ascending
51
- # ex:
52
- # params = { limit: RECORDS_LIMIT, order: :asc, .....}
53
- # ex: client.site_subscriptions(params)
54
- def site_subscriptions(**params)
55
- resp = request_api(path: 'subscriptions', params: params)
56
- handle_response!(resp)
57
- end
58
-
59
- # Create subscription(new purchase) by ssor_id(Recurly Account code)
60
- # { https://developers.recurly.com/api/v2021-02-25/index.html#operation/create_purchase }
61
- # @param ssor_id [String] Required, user SSOR_ID (i.e. Reculry Account Code)
62
- # @param plan_code [String] Required, plan_code or ID
63
- # @param billing_token [String] Required, token genereted by recurlyjs while checkout
64
- # @param gateway_code [String] Optional,
65
- # Payment gateway identifier to be used for the purchase transaction
66
- # ( required only if handling Braintree Multiple Merchant accounts )
67
- # @param account_info [Hash] Optional query parameters(user provided info while checkout)
68
- # :first_name [String], user First Name
69
- # :last_name [String], user Last Name
70
- # :email [String] user Email
71
- # TODO: fetch user email, first_name, last_name by ssor_id connecting to SsorClient
72
- # Better to pass first_name, last_name, email fetched from Checkout Page,
73
- # instead of extra DB call to SSOR to get the user details
74
- def create_subscription(ssor_id:, billing_token:, plan_code:,
75
- gateway_code: nil, account_info: {})
76
- account = { code: ssor_id,
77
- billing_info: { token_id: billing_token } }
78
- purchase_payload = { currency: 'USD',
79
- account: account.merge!(account_info),
80
- subscriptions: [{ plan_code: plan_code }] }
81
- purchase_payload[:gateway_code] = gateway_code if gateway_code
82
- path = 'purchases'
83
- resp = request_api(path: path, http_method: :post,
84
- payload: purchase_payload)
85
- handle_response!(resp, payload_name: :purchase_info, single_node: true)
86
- end
87
-
88
- # Cancel a subscription by its id or UUID
89
- # @param sub_id_or_uuid [String] Required
90
- # Subscription ID or UUID. For ID no prefix is used e.g. e28zov4fw0v2.
91
- # For UUID use prefix uuid-, e.g. uuid-123457890
92
- def cancel_subscription(sub_id_or_uuid)
93
- path = "subscriptions/#{sub_id_or_uuid}/cancel"
94
- resp = request_api(path: path, http_method: :put)
95
- handle_response!(resp, payload_name: :subscription_info, single_node: true)
96
- end
97
- end
98
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RecurlyApi
4
+ # API requests to fetch Subscriptions related data
5
+ class Client
6
+ # Fetch Account Subscriptions ---
7
+ # { https://developers.recurly.com/api/v2021-02-25/index.html#operation/list_account_subscriptions }
8
+ # @param ssor_id [String] Account ID(SsorID) or code.
9
+ # For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-gold+.
10
+ # For SsorID no prefix is required
11
+ # for other params ref documentation
12
+ def account_subscriptions(ssor_id:, recurly_query_params: {}, **optional_params)
13
+ path = "accounts/#{ssor_id}/subscriptions"
14
+ request_api(path_name: path, query_params: recurly_query_params, **optional_params) # cache_key_name: cache_key_name
15
+ end
16
+
17
+ # Checking if user already has subscription OR
18
+ # Checking if user has a subscription for particular plan by its code
19
+ # ---------------
20
+ # @param ssor_id [String] required, Recurly Account Code(i.e SsorID)
21
+ # @param plan_code [String] optional,
22
+ # - if present then checks if user has subscription for that particular plan by its code
23
+ # Usage ex:
24
+ # client.check_user_subscription(ssor_id: '4900-0272-6875', plan_code: '000150d03d')
25
+ # response => { :success=>true, :status=>200, :has_subscription=>true }
26
+ def check_user_subscription(ssor_id:, plan_code: nil)
27
+ resp = account_subscriptions(ssor_id: "code-#{ssor_id}")
28
+ if resp[:success]
29
+ has_subscription = false
30
+ subs = resp[:data]
31
+ has_subscription = true if subs.any?
32
+ if plan_code
33
+ sub = subs.detect { |s| s['plan']['code'].eql?(plan_code) }
34
+ has_subscription = sub ? true : false
35
+ end
36
+ { success: true, status: 200, has_subscription: has_subscription }
37
+ else
38
+ resp
39
+ end
40
+ end
41
+
42
+ # List a site's subscriptions
43
+ # { https://developers.recurly.com/api/v2021-02-25/#operation/list_account_subscriptions }
44
+ # @param recurly_query_params [Hash] Optional, Recurly query string parameters:
45
+ # i. :ids [String] Filter results by their IDs. Up to 200 IDs can be passed at once using
46
+ # commas as separators, e.g. <ids=h1at4d57xlmy,gyqgg0d3v9n1,jrsm5b4yefg6>.
47
+ # ii. :limit [Integer] Limit number of records 1-200.
48
+ # iii. :order [String] Sort order.
49
+ # iv: :sort [String] Sort field. You *really* only want to sort by <updated_at> in ascending
50
+ # ex:
51
+ # recurly_query_params = { limit: RECORDS_LIMIT, order: :asc, .....}
52
+ # ex: client.site_subscriptions(params)
53
+ def site_subscriptions(recurly_query_params: {}, **optional_params)
54
+ request_api(path_name: 'subscriptions', query_params: recurly_query_params, **optional_params)
55
+ end
56
+
57
+ # Create subscription(new purchase) by ssor_id(Recurly Account code)
58
+ # { https://developers.recurly.com/api/v2021-02-25/index.html#operation/create_purchase }
59
+ # @param ssor_id [String] Required, user SSOR_ID (i.e. Reculry Account Code)
60
+ # @param plan_code [String] Required, plan_code or ID
61
+ # @param billing_token [String] Required, token genereted by recurlyjs while checkout
62
+ # @param gateway_code [String] Optional,
63
+ # Payment gateway identifier to be used for the purchase transaction
64
+ # ( required only if handling Braintree Multiple Merchant accounts )
65
+ # @param account_info [Hash] Optional query parameters(user provided info while checkout)
66
+ # :first_name [String], user First Name
67
+ # :last_name [String], user Last Name
68
+ # :email [String] user Email
69
+ # TODO: fetch user email, first_name, last_name by ssor_id connecting to SsorClient
70
+ # Better to pass first_name, last_name, email fetched from Checkout Page,
71
+ # instead of extra DB call to SSOR to get the user details
72
+ def create_subscription(ssor_id:, billing_token:, plan_code:,
73
+ gateway_code: nil, account_info: {})
74
+ account = { code: ssor_id,
75
+ billing_info: { token_id: billing_token } }
76
+ purchase_payload = { currency: 'USD',
77
+ account: account.merge!(account_info),
78
+ subscriptions: [{ plan_code: plan_code }] }
79
+ purchase_payload[:gateway_code] = gateway_code if gateway_code
80
+ path = 'purchases'
81
+ request_api(path_name: path, http_method: :post,
82
+ payload: purchase_payload)
83
+ end
84
+
85
+ # Cancel a subscription by its id or UUID
86
+ # @param sub_id_or_uuid [String] Required
87
+ # Subscription ID or UUID. For ID no prefix is used e.g. e28zov4fw0v2.
88
+ # For UUID use prefix uuid-, e.g. uuid-123457890
89
+ def cancel_subscription(sub_id_or_uuid)
90
+ path = "subscriptions/#{sub_id_or_uuid}/cancel"
91
+ request_api(path_name: path, http_method: :put)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Common Exception Handler for all RestClient & Recurly API Server returned errors
4
+ module RecurlyApi
5
+ # Handles all possible exceptions that are returned by RestClient and Recurly Server and logs everything
6
+ module ExceptionHandler
7
+ def recurly_api_exception_handler(&block)
8
+ # block.call
9
+ yield
10
+ rescue RestClient::Unauthorized, RestClient::Forbidden => e
11
+ # https://www.rubydoc.info/gems/rest-client/1.6.7/frames#label-Result+handling
12
+ logger.error("#{logger_heading} ERROR: Access denied to Recurly API")
13
+ raise e.response
14
+ rescue RestClient::ImATeapot => e
15
+ logger.error("#{logger_heading} ERROR: Recurly Server is a teapot! # RFC 2324")
16
+ raise e.response
17
+ rescue RestClient::MovedPermanently,
18
+ RestClient::Found,
19
+ RestClient::TemporaryRedirect => e
20
+ logger.error("#{logger_heading} ERROR: Follow redirection- #{e.response.follow_redirection}")
21
+ raise e.response
22
+ rescue RestClient::ExceptionWithResponse => e
23
+ logger.error("#{logger_heading} ERROR: RestClient::ExceptionWithResponse")
24
+ raise e.response
25
+ rescue RestClient::Exception => e
26
+ logger.error("#{logger_heading} ERROR: RestClient::Exception")
27
+ raise e.response
28
+ # rescue StandardError => e # Note: don't rescue standard error
29
+ # logger.error("#{logger_heading} ERROR: Internal Server Error")
30
+ # # e.backtrace.join("\n ")
31
+ # raise e.message
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Logger for Gem
4
+ module RecurlyApi
5
+ # fallback to STDOUT for non-rails project
6
+ module Logging
7
+ # accessor setter method
8
+ attr_writer :logger
9
+
10
+ # Fallback to STDOUT logger for non-rails applications
11
+ # Usage examples to logging details in RecurlyApi:Client
12
+ # a. logger.info("I'm an info log")
13
+ # b. logger.warn("I'm a warn log")
14
+ # c. logger.error("I'm an error log: error message")
15
+ # d. logger.fatal("I'm a fatal log")
16
+ def logger
17
+ @logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
18
+ end
19
+
20
+ # Classical set method(instead used attr_writer)
21
+ # def logger=(logger)
22
+ # @logger = logger
23
+ # end
24
+
25
+ def logger_heading
26
+ 'tribune_recurly_api'
27
+ end
28
+
29
+ def logger_cache_heading
30
+ "#{logger_heading} CACHING"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Recurly rate limiting
4
+ # { https://developers.recurly.com/api/v2019-10-10/#section/Getting-Started/Limits }
5
+ # The rate limit is calculated over a sliding 5 minute window.
6
+ # This means a production site could make 4,000 requests within one minute and not hit the rate limit
7
+ # so long as the site made less than 1,000 requests during the prior 4 minute
8
+ # --------------
9
+ # Response Headers that returned related to rate limiting by Recurly.
10
+ # x_ratelimit_limit: The maximum number of requests available in the current time frame(5-minite window)
11
+ # x_ratelimit_remaining: the number of requests left for the 5-minute window
12
+ # x_ratelimit_reset: the remaining window before the rate limit resets, in UNIX Epoch seconds
13
+ module RecurlyApi
14
+ # Retry again in case rate limit exceeded
15
+ module RateLimiting
16
+ attr_accessor :ratelimit_retries
17
+ RATE_LIMIT_MAX_RETRIES = 3
18
+
19
+ # Check if rate limit exceeded or not using resp status code OR x_ratelimit_remaining header
20
+ def rate_limit_exceeded?
21
+ ratelimit_remaining = recurly_response.headers[:x_ratelimit_remaining]
22
+ logger.info("x_ratelimit_remaining---- : #{ratelimit_remaining}")
23
+ recurly_response.code.eql?(429) || (ratelimit_remaining && ratelimit_remaining.to_i < 1) # 1998
24
+ end
25
+
26
+ def retry_on_rate_limit_exceed
27
+ max_retries = ratelimit_retries # max count to retry if rate limit requests are exceeded.
28
+ retry_count = 0
29
+ delay = 1 # in seconds
30
+ begin
31
+ raise 'API Rate limit exceeded' if rate_limit_exceeded?
32
+ rescue StandardError => _e
33
+ logger.warn "API Rate limit exceeded retrying again. Retries left: #{max_retries - retry_count}"
34
+ sleep delay += retry_count
35
+ retry_count += 1
36
+ retry if retry_count < max_retries
37
+ end
38
+ end
39
+
40
+ def rate_limit_exceed_response
41
+ resp_headers = recurly_response.headers
42
+ rate_limit_info = { limit: resp_headers[:x_ratelimit_limit],
43
+ remaining: resp_headers[:x_ratelimit_remaining],
44
+ reset_time_seconds: resp_headers[:x_ratelimit_reset],
45
+ reset_time: DateTime.strptime(resp_headers[:x_ratelimit_reset], '%s') }
46
+ { success: false, status_code: 429,
47
+ error: 'Recurly API Rate limit exceeded',
48
+ message: 'Too Many Requests, See rate-limiting for more info',
49
+ rate_limit_info: rate_limit_info }
50
+ end
51
+ end
52
+ end
@@ -1,6 +1,18 @@
1
- # frozen_string_literal: true
2
-
3
- # Gem visioning
4
- module RecurlyApi
5
- VERSION = '0.4.3'
6
- end
1
+ # frozen_string_literal: true
2
+
3
+ # Gem visioning
4
+ # fallow below ref doc to release new version of Gem
5
+ # -https://guides.rubygems.org/patterns/
6
+ module RecurlyApi
7
+ # MAJOR = 1
8
+ # MINOR = 0
9
+ # TINY = 4
10
+ # PRE = nil
11
+ #
12
+ # STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.').freeze
13
+ #
14
+ # def self.to_s
15
+ # STRING
16
+ # end
17
+ VERSION = '1.0.0'
18
+ end
metadata CHANGED
@@ -1,57 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tribune_recurly_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - udevulapally
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-02 00:00:00.000000000 Z
11
+ date: 2021-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rake
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rest-client
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - "~>"
18
32
  - !ruby/object:Gem::Version
19
- version: '10.0'
20
- type: :development
33
+ version: 1.8.0
34
+ type: :runtime
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
38
  - - "~>"
25
39
  - !ruby/object:Gem::Version
26
- version: '10.0'
40
+ version: 1.8.0
27
41
  - !ruby/object:Gem::Dependency
28
- name: rspec
42
+ name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '3.0'
47
+ version: '10.0'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: '3.0'
54
+ version: '10.0'
41
55
  - !ruby/object:Gem::Dependency
42
- name: rest-client
56
+ name: rspec
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: 1.8.0
48
- type: :runtime
61
+ version: '3.0'
62
+ type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: 1.8.0
68
+ version: '3.0'
55
69
  description: Wrapper for connecting to Recurly V3 API.
56
70
  email:
57
71
  - udevulapally@tribpub.com
@@ -60,17 +74,22 @@ extensions: []
60
74
  extra_rdoc_files: []
61
75
  files:
62
76
  - lib/recurly_api.rb
77
+ - lib/recurly_api/caching.rb
63
78
  - lib/recurly_api/client.rb
64
79
  - lib/recurly_api/client/accounts.rb
65
80
  - lib/recurly_api/client/other_requests.rb
66
81
  - lib/recurly_api/client/plans.rb
67
82
  - lib/recurly_api/client/subscriptions.rb
83
+ - lib/recurly_api/exception_handler.rb
84
+ - lib/recurly_api/logging.rb
85
+ - lib/recurly_api/rate_limiting.rb
68
86
  - lib/recurly_api/version.rb
69
87
  - lib/tribune_recurly_api.rb
70
88
  homepage:
71
89
  licenses: []
72
90
  metadata:
73
91
  allowed_push_host: https://rubygems.org/
92
+ homepage_uri: https://diggit.int.tribops.com/gems/tribune_recurly_api/
74
93
  post_install_message:
75
94
  rdoc_options: []
76
95
  require_paths: