fulfil-io 0.4.7 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +77 -15
- data/lib/fulfil/client.rb +34 -19
- data/lib/fulfil/error.rb +29 -0
- data/lib/fulfil/interactive_report.rb +12 -5
- data/lib/fulfil/rate_limit.rb +28 -0
- data/lib/fulfil/rate_limit_headers.rb +48 -0
- data/lib/fulfil/response_handler.rb +61 -0
- data/lib/fulfil/response_parser.rb +2 -3
- data/lib/fulfil/version.rb +1 -1
- data/lib/fulfil.rb +11 -1
- metadata +50 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c58e4104ae9106550bca7c000cbf3f576f551c9e4512cee93dc74dd0aa5604f
|
4
|
+
data.tar.gz: ee0644f88360b5356efa034833772a54d207ce4be7e81040219e8d66019ff301
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d697a2153dcbcd379621b59bc7ef8af428c4f6ecc2445d454afc38eddb34247430279136f758fe52e538abcda923c386dbc4d7f2af00c27e9e41c26f71a4c5e2
|
7
|
+
data.tar.gz: e6e891f839dd472f24f1dc89ce1189cb80b9218ac09c35c4141c0dd0b575097ecb0d238d47de8e688bc69c3298da0f4e1b2478ebfed1010d139c3fb4087f0ccf
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,36 +1,40 @@
|
|
1
|
-
|
1
|
+
[![Tests](https://github.com/knowndecimal/fulfil/actions/workflows/tests.yml/badge.svg)](https://github.com/knowndecimal/fulfil/actions/workflows/tests.yml)
|
2
2
|
|
3
|
-
|
3
|
+
# Fulfil.io Rubygem
|
4
4
|
|
5
|
-
[Fulfil.io](https://fulfil.io)
|
5
|
+
A Ruby library for the [Fulfil.io](https://fulfil.io) API.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
9
|
Add this line to your application's Gemfile:
|
10
10
|
|
11
11
|
```ruby
|
12
|
-
gem 'fulfil-io', require: 'fulfil'
|
12
|
+
gem 'fulfil-io', require: 'fulfil'
|
13
13
|
```
|
14
14
|
|
15
15
|
And then execute:
|
16
16
|
|
17
|
-
|
17
|
+
```shell
|
18
|
+
$ bundle install
|
19
|
+
```
|
18
20
|
|
19
21
|
Or install it yourself as:
|
20
22
|
|
21
|
-
|
23
|
+
```shell
|
24
|
+
$ gem install fulfil-io
|
25
|
+
```
|
22
26
|
|
23
27
|
## Usage
|
24
28
|
|
25
29
|
Environment variables:
|
26
30
|
|
27
|
-
- FULFIL_SUBDOMAIN - required to
|
28
|
-
-
|
29
|
-
- FULFIL_API_KEY
|
31
|
+
- **FULFIL_SUBDOMAIN:** - always required to use the gem.
|
32
|
+
- **FULFIL_OAUTH_TOKEN:** required for oauth bearer authentication
|
33
|
+
- **FULFIL_API_KEY:** required for authentication via the `X-API-KEY` request header
|
30
34
|
|
31
|
-
**Note:** When
|
35
|
+
> **Note:** When `FULFIL_OAUTH_TOKEN` is present, the `FULFIL_API_KEY` will be ignored. So,
|
32
36
|
if oauth doesn't work, returning an Unauthorized error, to use the
|
33
|
-
FULFIL_API_KEY
|
37
|
+
`FULFIL_API_KEY`, the `FULFIL_OAUTH_TOKEN` shouldn't be specified.
|
34
38
|
|
35
39
|
```ruby
|
36
40
|
require 'fulfil' # this is necessary only in case of running without bundler
|
@@ -123,16 +127,74 @@ report = Fulfil::Report.new(client: fulfil, report_name: 'account.tax.summary.ir
|
|
123
127
|
report.execute(start_date: Date.new(2020, 12, 1), end_date: Date.new(2020, 12, 31))
|
124
128
|
```
|
125
129
|
|
130
|
+
## Rate limits
|
131
|
+
|
132
|
+
Fulfil's API applies rate limits to the API requests that it receives. Every request is subject to throttling under the general limits. In addition, there are resource-based rate limits and throttles.
|
133
|
+
|
134
|
+
This gem exposes an API for checking your current rate limits (note: the gem only knows about the rate limit after a request to Fulfil's API has been made).
|
135
|
+
|
136
|
+
Whenever you reached the rate limit, the `Fulfil::RateLimitExceeded` exception is being raised. You can use the information on the `Fulfil.rate_limit` to find out what to do next.
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
$ Fulfil.rate_limit.requests_left?
|
140
|
+
=> true
|
141
|
+
|
142
|
+
# The maximum number of requests you're permitted to make per second.
|
143
|
+
$ Fulfil.rate_limit.limit
|
144
|
+
=> 9
|
145
|
+
|
146
|
+
# The time at which the current rate limit window resets in UTC epoch seconds.
|
147
|
+
$ Fulfil.rate_limit.resets_at
|
148
|
+
=> #<DateTime: 2022-01-21T16:36:01-04:00 />
|
149
|
+
```
|
126
150
|
## Development
|
127
151
|
|
128
152
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
129
153
|
`rake test` to run the tests. You can also run `bin/console` for an interactive
|
130
154
|
prompt that will allow you to experiment.
|
131
155
|
|
132
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
133
|
-
|
134
|
-
|
135
|
-
|
156
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
157
|
+
|
158
|
+
### Release a new version
|
159
|
+
|
160
|
+
We're following semver for the release process of this gem. Make sure to apply the correct semver version for a new release.
|
161
|
+
|
162
|
+
To release a new version, run the `bin/release x.x.x`. That's it.
|
163
|
+
|
164
|
+
> **NOTE:** You don't have to add a v to the version you want to release. The release script will handle that for you.
|
165
|
+
|
166
|
+
### Testing
|
167
|
+
|
168
|
+
For non-client tests, create the test class or case.
|
169
|
+
|
170
|
+
For client tests, you'll need to add a couple steps. If running against a real
|
171
|
+
backend, you'll need to provide a couple of environment variables:
|
172
|
+
`FULFIL_SUBDOMAIN` and `FULFIL_OAUTH_TOKEN`. Additionally, pass `debug: true` to the
|
173
|
+
client instance in the test. This will output the response body. Webmock will
|
174
|
+
probably complain that real requests aren't allowed at this point, offering you
|
175
|
+
the stub. We don't need most of that.
|
176
|
+
|
177
|
+
We will need to capture the response body as JSON and store it in the
|
178
|
+
`test/fixtures` directory. Formatted for readability, please. You'll also need
|
179
|
+
to make note of the path and body of the request. Once you have that, you can
|
180
|
+
generate your stub.
|
181
|
+
|
182
|
+
To stub a request, use (or create) the helper method based on the verb. For
|
183
|
+
example, to stub a `GET` request, use `stub_fulfil_get`. Here's an example:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
def test_find_one
|
187
|
+
stub_fulfil_get('sale.sale/213112', 'sale_sale')
|
188
|
+
|
189
|
+
client = Fulfil::Client.new
|
190
|
+
response = client.find_one(model: 'sale.sale', id: 213_112)
|
191
|
+
|
192
|
+
assert_equal 213_112, response['id']
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
`stub_fulfil_get` takes two arguments: the URL path (after `/api/v2/model/`)
|
197
|
+
and the fixture file name to be returned.
|
136
198
|
|
137
199
|
## Contributing
|
138
200
|
|
data/lib/fulfil/client.rb
CHANGED
@@ -7,9 +7,14 @@ require 'fulfil/response_parser'
|
|
7
7
|
module Fulfil
|
8
8
|
SUBDOMAIN = ENV['FULFIL_SUBDOMAIN']
|
9
9
|
API_KEY = ENV['FULFIL_API_KEY']
|
10
|
-
OAUTH_TOKEN = ENV['FULFIL_TOKEN']
|
11
10
|
|
12
11
|
class Client
|
12
|
+
class InvalidClientError < StandardError
|
13
|
+
def message
|
14
|
+
'Client is not configured correctly.'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
13
18
|
class NotAuthorizedError < StandardError; end
|
14
19
|
|
15
20
|
class UnknownHTTPError < StandardError; end
|
@@ -18,12 +23,22 @@ module Fulfil
|
|
18
23
|
|
19
24
|
class ResponseError < StandardError; end
|
20
25
|
|
21
|
-
def initialize(subdomain: SUBDOMAIN, token:
|
26
|
+
def initialize(subdomain: SUBDOMAIN, token: oauth_token, headers: { 'X-API-KEY' => API_KEY }, debug: false)
|
22
27
|
@subdomain = subdomain
|
23
28
|
@token = token
|
24
29
|
@debug = debug
|
25
30
|
@headers = headers
|
26
31
|
@headers.delete('X-API-KEY') if @token
|
32
|
+
|
33
|
+
raise InvalidClientError if invalid?
|
34
|
+
end
|
35
|
+
|
36
|
+
def invalid?
|
37
|
+
@subdomain.nil? || @subdomain.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
def valid?
|
41
|
+
!invalid?
|
27
42
|
end
|
28
43
|
|
29
44
|
def find(model:, ids: [], id: nil, fields: %w[id rec_name])
|
@@ -96,6 +111,15 @@ module Fulfil
|
|
96
111
|
|
97
112
|
private
|
98
113
|
|
114
|
+
def oauth_token
|
115
|
+
if ENV['FULFIL_TOKEN']
|
116
|
+
puts "You're using an deprecated environment variable. Please update your " \
|
117
|
+
'FULFIL_TOKEN to FULFIL_OAUTH_TOKEN.'
|
118
|
+
end
|
119
|
+
|
120
|
+
ENV['FULFIL_OAUTH_TOKEN'] || ENV['FULFIL_TOKEN']
|
121
|
+
end
|
122
|
+
|
99
123
|
def parse(result: nil, results: [])
|
100
124
|
if result
|
101
125
|
parse_single(result: result)
|
@@ -125,19 +149,12 @@ module Fulfil
|
|
125
149
|
end
|
126
150
|
|
127
151
|
def request(endpoint:, verb: :get, **args)
|
152
|
+
raise InvalidClientError if invalid?
|
153
|
+
|
128
154
|
response = client.request(verb, endpoint, args)
|
155
|
+
Fulfil::ResponseHandler.new(response).verify!
|
129
156
|
|
130
|
-
|
131
|
-
response.parse
|
132
|
-
elsif response.code == 204
|
133
|
-
[]
|
134
|
-
elsif response.code == 401
|
135
|
-
error = response.parse
|
136
|
-
raise NotAuthorizedError, "Not authorized: #{error['error']}: #{error['error_description']}"
|
137
|
-
else
|
138
|
-
puts response.body.to_s
|
139
|
-
raise Error, 'Error encountered while processing response:'
|
140
|
-
end
|
157
|
+
response.parse
|
141
158
|
rescue HTTP::Error => e
|
142
159
|
puts e
|
143
160
|
raise UnknownHTTPError, 'Unhandled HTTP error encountered'
|
@@ -150,12 +167,10 @@ module Fulfil
|
|
150
167
|
end
|
151
168
|
|
152
169
|
def client
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
@client = @client.headers(@headers)
|
158
|
-
@client
|
170
|
+
client = HTTP.use(logging: @debug ? { logger: Logger.new(STDOUT) } : {})
|
171
|
+
client = client.auth("Bearer #{@token}") if @token
|
172
|
+
client = client.headers(@headers)
|
173
|
+
client
|
159
174
|
end
|
160
175
|
end
|
161
176
|
end
|
data/lib/fulfil/error.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fulfil
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
class RateLimitExceeded < Error; end
|
7
|
+
|
8
|
+
# The `Fulfil::HttpError` is raised whenever an API request returns a 400+ HTTP status code.
|
9
|
+
# See `Fulfil::ResponseHandler` for more information.
|
10
|
+
class HttpError < Error
|
11
|
+
attr_reader :metadata
|
12
|
+
|
13
|
+
def initialize(message, metadata = {})
|
14
|
+
@metadata = metadata
|
15
|
+
super(message)
|
16
|
+
end
|
17
|
+
|
18
|
+
class BadRequest < HttpError; end
|
19
|
+
class AuthorizationRequired < HttpError; end
|
20
|
+
class PaymentRequired < HttpError; end
|
21
|
+
class Forbidden < HttpError; end
|
22
|
+
class NotFound < HttpError; end
|
23
|
+
class MethodNotAllowed < HttpError; end
|
24
|
+
class NotAccepted < HttpError; end
|
25
|
+
class UnprocessableEntity < HttpError; end
|
26
|
+
class TooManyRequests < HttpError; end
|
27
|
+
class InternalServerError < HttpError; end
|
28
|
+
end
|
29
|
+
end
|
@@ -5,13 +5,20 @@ module Fulfil
|
|
5
5
|
@report = report
|
6
6
|
end
|
7
7
|
|
8
|
-
def execute(
|
8
|
+
def execute(**params)
|
9
|
+
body = {}
|
10
|
+
|
11
|
+
params.each do |key, value|
|
12
|
+
body[key] = if value.is_a?(Date)
|
13
|
+
serialize_date(value)
|
14
|
+
else
|
15
|
+
value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
9
19
|
@client.interactive_report(
|
10
20
|
endpoint: report_url,
|
11
|
-
body: [
|
12
|
-
start_date: serialize_date(start_date),
|
13
|
-
end_date: serialize_date(end_date)
|
14
|
-
}]
|
21
|
+
body: [body]
|
15
22
|
)
|
16
23
|
end
|
17
24
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fulfil
|
4
|
+
# The `Fulfil::RateLimit` allows clients to keep track of their API usage and
|
5
|
+
# to analyze Fulfil's response HTTP headers.
|
6
|
+
class RateLimit
|
7
|
+
attr_accessor :limit, :requests_left, :resets_at
|
8
|
+
|
9
|
+
# Analyses the rate limit based on the response headers from Fulfil.
|
10
|
+
# @param headers [HTTP::Headers] The HTTP response headers from Fulfil.
|
11
|
+
# @return [Fulfil::RateLimit]
|
12
|
+
def analyse!(headers)
|
13
|
+
rate_limit_headers = RateLimitHeaders.new(headers)
|
14
|
+
|
15
|
+
self.limit = rate_limit_headers.limit
|
16
|
+
self.requests_left = rate_limit_headers.requests_left
|
17
|
+
self.resets_at = rate_limit_headers.resets_at
|
18
|
+
|
19
|
+
raise Fulfil::RateLimitExceeded unless requests_left?
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns whether there are any requests left in the current rate limit window.
|
23
|
+
# @return [Boolean]
|
24
|
+
def requests_left?
|
25
|
+
requests_left&.positive?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fulfil
|
4
|
+
# The `Fulfil::RateLimitHeaders` parses Fulfil HTTP rate limit headers and
|
5
|
+
# formats them to a more usable format.
|
6
|
+
class RateLimitHeaders
|
7
|
+
# Test suites might mock (or at least should mock) the requests to Fulfil.
|
8
|
+
# However, most of these test suites will not mock the response headers.
|
9
|
+
# To make sure those test suites don't break, we're setting some defaults for them.
|
10
|
+
DEFAULT_REQUEST_LIMIT = 10
|
11
|
+
DEFAULT_REQUESTS_LEFT = 9
|
12
|
+
DEFAULT_RESETS_AT = nil
|
13
|
+
|
14
|
+
attr_reader :limit, :requests_left, :resets_at
|
15
|
+
|
16
|
+
def initialize(headers = {})
|
17
|
+
self.limit = headers['X-RateLimit-Limit'] || DEFAULT_REQUEST_LIMIT
|
18
|
+
self.requests_left = headers['X-RateLimit-Remaining'] || DEFAULT_REQUESTS_LEFT
|
19
|
+
self.resets_at = headers['X-RateLimit-Reset'] || DEFAULT_RESETS_AT
|
20
|
+
end
|
21
|
+
|
22
|
+
# Sets the maximum number of requests you're permitted to make per second.
|
23
|
+
# @param value [String] The maximum number of requests per second.
|
24
|
+
# @return [Integer] The maximum number of requests per second.
|
25
|
+
def limit=(value)
|
26
|
+
@limit = value.to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
# Sets number of requests remaining in the current rate limit window.
|
30
|
+
# @param value [String] The remaining number of requests for the current time window.
|
31
|
+
# @return [Integer] The remaining number of requests for the current time window.
|
32
|
+
def requests_left=(value)
|
33
|
+
@requests_left = value.to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
# Sets the time at which the current rate limit window resets in UTC epoch seconds.
|
37
|
+
# @param value [Integer|nil] Time as an integer in UTC epoch seconds.
|
38
|
+
# @return [DataTime|nil] The moment the rate limit resets.
|
39
|
+
def resets_at=(value)
|
40
|
+
@resets_at =
|
41
|
+
if value.nil?
|
42
|
+
nil
|
43
|
+
else
|
44
|
+
Time.at(value.to_i).utc.to_datetime
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fulfil
|
4
|
+
# The `Fulfil::ResponseHandler` is parses the HTTP response from Fulfil. If it
|
5
|
+
# encounters an HTTP status code that indicates an error, it will raise an internal
|
6
|
+
# exception that the consumer can catch.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# Fulfil::ResponseHandler.new(@response).verify!
|
10
|
+
# => true
|
11
|
+
#
|
12
|
+
# Fulfil::ResponseHandler.new(@response).verify!
|
13
|
+
# => Fulfil::Error::BadRequest
|
14
|
+
class ResponseHandler
|
15
|
+
HTTP_ERROR_CODES = {
|
16
|
+
400 => Fulfil::HttpError::BadRequest,
|
17
|
+
401 => Fulfil::HttpError::AuthorizationRequired,
|
18
|
+
402 => Fulfil::HttpError::PaymentRequired,
|
19
|
+
403 => Fulfil::HttpError::Forbidden,
|
20
|
+
404 => Fulfil::HttpError::NotFound,
|
21
|
+
405 => Fulfil::HttpError::MethodNotAllowed,
|
22
|
+
406 => Fulfil::HttpError::NotAccepted,
|
23
|
+
422 => Fulfil::HttpError::UnprocessableEntity,
|
24
|
+
429 => Fulfil::HttpError::TooManyRequests,
|
25
|
+
500 => Fulfil::HttpError::InternalServerError
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
def initialize(response)
|
29
|
+
@response = response
|
30
|
+
@status_code = response.code
|
31
|
+
end
|
32
|
+
|
33
|
+
def verify!
|
34
|
+
verify_rate_limits!
|
35
|
+
verify_http_status_code!
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def verify_rate_limits!
|
41
|
+
Fulfil.rate_limit.analyse!(@response.headers)
|
42
|
+
end
|
43
|
+
|
44
|
+
def verify_http_status_code!
|
45
|
+
return true unless @status_code >= 400
|
46
|
+
|
47
|
+
raise HTTP_ERROR_CODES.fetch(@status_code, Fulfil::HttpError).new(
|
48
|
+
response_body['error_description'],
|
49
|
+
{
|
50
|
+
body: @response.body,
|
51
|
+
headers: @response.headers,
|
52
|
+
status: @response.status
|
53
|
+
}
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def response_body
|
58
|
+
@response_body ||= @response.parse
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -41,7 +41,7 @@ module Fulfil
|
|
41
41
|
def self.group(key_value_tuples)
|
42
42
|
key_value_tuples
|
43
43
|
.group_by { |kv_tuple| kv_tuple[0][0] }
|
44
|
-
.map
|
44
|
+
.map do |group_key, kv_tuples|
|
45
45
|
if kv_tuples.length == 1
|
46
46
|
[group_key, mapped_value_field(value: kv_tuples[0][1])]
|
47
47
|
else
|
@@ -49,7 +49,7 @@ module Fulfil
|
|
49
49
|
attrs = kv_tuples[1..-1].map { |tuple| [tuple[0][1..-1], tuple[1]] }
|
50
50
|
[group_key, [['id', id[1]]].concat(group(attrs)).to_h]
|
51
51
|
end
|
52
|
-
|
52
|
+
end
|
53
53
|
end
|
54
54
|
|
55
55
|
def self.parse(item:)
|
@@ -57,5 +57,4 @@ module Fulfil
|
|
57
57
|
group(key_value_tuples).to_h
|
58
58
|
end
|
59
59
|
end
|
60
|
-
|
61
60
|
end
|
data/lib/fulfil/version.rb
CHANGED
data/lib/fulfil.rb
CHANGED
@@ -1,9 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'fulfil/version'
|
4
|
+
require 'fulfil/error'
|
2
5
|
require 'fulfil/client'
|
3
6
|
require 'fulfil/model'
|
4
7
|
require 'fulfil/interactive_report'
|
8
|
+
require 'fulfil/response_handler'
|
5
9
|
require 'fulfil/response_parser'
|
6
10
|
|
11
|
+
# Rate limiting
|
12
|
+
require 'fulfil/rate_limit'
|
13
|
+
require 'fulfil/rate_limit_headers'
|
14
|
+
|
7
15
|
module Fulfil
|
8
|
-
|
16
|
+
def self.rate_limit
|
17
|
+
@rate_limit ||= RateLimit.new
|
18
|
+
end
|
9
19
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fulfil-io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Moore
|
@@ -9,22 +9,28 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-03-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: http
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: 4.4.1
|
21
|
+
- - "<"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 5.1.0
|
21
24
|
type: :runtime
|
22
25
|
prerelease: false
|
23
26
|
version_requirements: !ruby/object:Gem::Requirement
|
24
27
|
requirements:
|
25
|
-
- - "
|
28
|
+
- - ">="
|
26
29
|
- !ruby/object:Gem::Version
|
27
30
|
version: 4.4.1
|
31
|
+
- - "<"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 5.1.0
|
28
34
|
- !ruby/object:Gem::Dependency
|
29
35
|
name: bundler
|
30
36
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,6 +101,40 @@ dependencies:
|
|
95
101
|
- - ">="
|
96
102
|
- !ruby/object:Gem::Version
|
97
103
|
version: '0'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: webmock
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
- !ruby/object:Gem::Dependency
|
119
|
+
name: dotenv
|
120
|
+
requirement: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '2.7'
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: 2.7.6
|
128
|
+
type: :development
|
129
|
+
prerelease: false
|
130
|
+
version_requirements: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - "~>"
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '2.7'
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: 2.7.6
|
98
138
|
description:
|
99
139
|
email:
|
100
140
|
- chris@knowndecimal.com
|
@@ -111,9 +151,13 @@ files:
|
|
111
151
|
- Rakefile
|
112
152
|
- lib/fulfil.rb
|
113
153
|
- lib/fulfil/client.rb
|
154
|
+
- lib/fulfil/error.rb
|
114
155
|
- lib/fulfil/interactive_report.rb
|
115
156
|
- lib/fulfil/model.rb
|
116
157
|
- lib/fulfil/query.rb
|
158
|
+
- lib/fulfil/rate_limit.rb
|
159
|
+
- lib/fulfil/rate_limit_headers.rb
|
160
|
+
- lib/fulfil/response_handler.rb
|
117
161
|
- lib/fulfil/response_parser.rb
|
118
162
|
- lib/fulfil/version.rb
|
119
163
|
homepage: https://github.com/knowndecimal/fulfil
|
@@ -128,14 +172,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
172
|
requirements:
|
129
173
|
- - ">="
|
130
174
|
- !ruby/object:Gem::Version
|
131
|
-
version: 2.
|
175
|
+
version: '2.4'
|
132
176
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
177
|
requirements:
|
134
178
|
- - ">="
|
135
179
|
- !ruby/object:Gem::Version
|
136
180
|
version: '0'
|
137
181
|
requirements: []
|
138
|
-
rubygems_version: 3.
|
182
|
+
rubygems_version: 3.3.7
|
139
183
|
signing_key:
|
140
184
|
specification_version: 4
|
141
185
|
summary: Interact with the Fulfil.io API
|