fulfil-io 0.4.4 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7be62208f07b3dabac64da70cd97d7ef5d5e513a363d830206ffc6ad25ebffc5
4
- data.tar.gz: ddba02ac333a132dcce3886f51b35865d58f4a999adba7f1f10a9ee3cec5e344
3
+ metadata.gz: 1100e113c9323c0fa742ece65d8a41923c060c382b1b97ad6c50ad20c550b5a9
4
+ data.tar.gz: 80535f7bea24218d958812bbd8e4ab789ce966c346e5dfa891369160fc04e536
5
5
  SHA512:
6
- metadata.gz: 02e3e8e5ae922598b1971b96ba26ba2a17c6f05cc4c2ea319bc0e3d69b75fdfc9115ee860b43d4c2965e292f80470ea17d9c07819d0d30aef03610fa1611ae85
7
- data.tar.gz: 000740a6f8cff338590329900671bf297acfd41c83f38c5197bac711706ac934b37e39e55ec5683575b6e9ffbfead3f73b6d766afb4b0d8a5e357aa6db9c6c4b
6
+ metadata.gz: b1ea6e403ab80bb108a90dff5f7ad58c39c228ed610ce497c631b5ae45a3ef5d2912100acd95ee33d7f2aa7c5609b71300c7a2322e1b2efcec688646573bf8da
7
+ data.tar.gz: d63d23350f0775bac73f28fdbabd0645798ed28a0466e7851ade38328a9f17e7a75982bc6bada6c1d951e4eb3ed0a4ffd872c79a6dc3c848f4308ddcfd11db16
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## 0.4.9
2
+
3
+ * Add client tests and stub with Webmock.
4
+
5
+ ## 0.4.8
6
+
7
+ * Feature: Allow more params with InteractiveReport.
8
+
9
+ ## 0.4.7
10
+
11
+ * Bugfix: Accidentally removed the model parameter from the URL.
12
+
13
+ ## 0.4.6
14
+
15
+ * Add InteractiveReport support.
16
+
17
+ ## 0.4.5
18
+
19
+ * Add #delete to client.
20
+ * Set up Dependabot on GitHub.
21
+
1
22
  ## 0.4.4
2
23
 
3
24
  * Pin http dependency to ~> 4.4.0. 5.0+ introduces a frozen string error.
data/README.md CHANGED
@@ -109,6 +109,19 @@ sale['channel'] = 4
109
109
 
110
110
  fulfil.put(model: sale_model, body: sale)
111
111
  ```
112
+ ### Interactive Reports
113
+
114
+ As of v0.4.6, interactive report support exists in a basic form.
115
+ You're able to execute reports with basic params. Responses are
116
+ transformed to JSON structures.
117
+
118
+ ```ruby
119
+ fulfil = Fulfil::Client.new
120
+
121
+ report = Fulfil::Report.new(client: fulfil, report_name: 'account.tax.summary.ireport')
122
+
123
+ report.execute(start_date: Date.new(2020, 12, 1), end_date: Date.new(2020, 12, 31))
124
+ ```
112
125
 
113
126
  ## Development
114
127
 
@@ -116,10 +129,48 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
116
129
  `rake test` to run the tests. You can also run `bin/console` for an interactive
117
130
  prompt that will allow you to experiment.
118
131
 
119
- To install this gem onto your local machine, run `bundle exec rake install`. To
120
- release a new version, update the version number in `version.rb`, and then run
121
- `bundle exec rake release`, which will create a git tag for the version, push
122
- git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
132
+ To install this gem onto your local machine, run `bundle exec rake install`.
133
+
134
+ ### Release a new version
135
+
136
+ We're following semver for the release process of this gem. Make sure to apply the correct semver version for a new release.
137
+
138
+ To release a new version, run the `bin/release x.x.x`. That's it.
139
+
140
+ > **NOTE:** You don't have to add a v to the version you want to release. The release script will handle that for you.
141
+
142
+ ### Testing
143
+
144
+ For non-client tests, create the test class or case.
145
+
146
+ For client tests, you'll need to add a couple steps. If running against a real
147
+ backend, you'll need to provide a couple of environment variables:
148
+ `FULFIL_SUBDOMAIN` and `FULFIL_TOKEN`. Additionally, pass `debug: true` to the
149
+ client instance in the test. This will output the response body. Webmock will
150
+ probably complain that real requests aren't allowed at this point, offering you
151
+ the stub. We don't need most of that.
152
+
153
+ We will need to capture the response body as JSON and store it in the
154
+ `test/fixtures` directory. Formatted for readability, please. You'll also need
155
+ to make note of the path and body of the request. Once you have that, you can
156
+ generate your stub.
157
+
158
+ To stub a request, use (or create) the helper method based on the verb. For
159
+ example, to stub a `GET` request, use `stub_fulfil_get`. Here's an example:
160
+
161
+ ```ruby
162
+ def test_find_one
163
+ stub_fulfil_get('sale.sale/213112', 'sale_sale')
164
+
165
+ client = Fulfil::Client.new
166
+ response = client.find_one(model: 'sale.sale', id: 213_112)
167
+
168
+ assert_equal 213_112, response['id']
169
+ end
170
+ ```
171
+
172
+ `stub_fulfil_get` takes two arguments: the URL path (after `/api/v2/model/`)
173
+ and the fixture file name to be returned.
123
174
 
124
175
  ## Contributing
125
176
 
data/lib/fulfil/client.rb CHANGED
@@ -10,9 +10,18 @@ module Fulfil
10
10
  OAUTH_TOKEN = ENV['FULFIL_TOKEN']
11
11
 
12
12
  class Client
13
+ class InvalidClientError < StandardError
14
+ def message
15
+ 'Client is not configured correctly.'
16
+ end
17
+ end
18
+
13
19
  class NotAuthorizedError < StandardError; end
20
+
14
21
  class UnknownHTTPError < StandardError; end
22
+
15
23
  class ConnectionError < StandardError; end
24
+
16
25
  class ResponseError < StandardError; end
17
26
 
18
27
  def initialize(subdomain: SUBDOMAIN, token: OAUTH_TOKEN, headers: { 'X-API-KEY' => API_KEY }, debug: false)
@@ -21,6 +30,16 @@ module Fulfil
21
30
  @debug = debug
22
31
  @headers = headers
23
32
  @headers.delete('X-API-KEY') if @token
33
+
34
+ raise InvalidClientError if invalid?
35
+ end
36
+
37
+ def invalid?
38
+ @subdomain.nil? || @subdomain.empty?
39
+ end
40
+
41
+ def valid?
42
+ !invalid?
24
43
  end
25
44
 
26
45
  def find(model:, ids: [], id: nil, fields: %w[id rec_name])
@@ -71,13 +90,26 @@ module Fulfil
71
90
  parse(results: results)
72
91
  end
73
92
 
74
- def put(model:, id:, endpoint: nil, body: {})
93
+ def put(model: nil, id: nil, endpoint: nil, body: {})
75
94
  uri = URI(model_url(model: model, id: id, endpoint: endpoint))
76
95
 
77
96
  result = request(verb: :put, endpoint: uri, json: body)
78
97
  parse(result: result)
79
98
  end
80
99
 
100
+ def delete(model:, id:)
101
+ uri = URI(model_url(model: model, id: id))
102
+
103
+ result = request(verb: :delete, endpoint: uri)
104
+ parse(result: result)
105
+ end
106
+
107
+ def interactive_report(endpoint:, body: nil)
108
+ uri = URI("#{base_url}/model/#{endpoint}")
109
+ result = request(verb: :put, endpoint: uri, json: body)
110
+ parse(result: result)
111
+ end
112
+
81
113
  private
82
114
 
83
115
  def parse(result: nil, results: [])
@@ -96,34 +128,33 @@ module Fulfil
96
128
  results.map { |result| Fulfil::ResponseParser.parse(item: result) }
97
129
  end
98
130
 
131
+ def domain
132
+ "https://#{@subdomain}.fulfil.io"
133
+ end
134
+
99
135
  def base_url
100
- "https://#{@subdomain}.fulfil.io/api/v2/model"
136
+ [domain, 'api', 'v2'].join('/')
101
137
  end
102
138
 
103
139
  def model_url(model:, id: nil, endpoint: nil)
104
- [base_url, model, id, endpoint].compact.join('/')
140
+ [base_url, 'model', model, id, endpoint].compact.join('/')
105
141
  end
106
142
 
107
- def request(verb: :get, endpoint:, **args)
143
+ def request(endpoint:, verb: :get, **args)
144
+ raise InvalidClientError if invalid?
145
+
108
146
  response = client.request(verb, endpoint, args)
147
+ Fulfil::ResponseHandler.new(response).verify!
109
148
 
110
- if response.status.ok? || response.status.created?
111
- response.parse
112
- elsif response.code == 401
113
- error = response.parse
114
- raise NotAuthorizedError, "Not authorized: #{error['error']}: #{error['error_description']}"
115
- else
116
- puts response.body.to_s
117
- raise Error, 'Error encountered while processing response:'
118
- end
149
+ response.parse
119
150
  rescue HTTP::Error => e
120
151
  puts e
121
152
  raise UnknownHTTPError, 'Unhandled HTTP error encountered'
122
153
  rescue HTTP::ConnectionError => e
123
154
  puts "Couldn't connect"
124
155
  raise ConnectionError, "Can't connect to #{base_url}"
125
- rescue HTTP::ResponseError => ex
126
- raise ResponseError, "Can't process response: #{ex}"
156
+ rescue HTTP::ResponseError => e
157
+ raise ResponseError, "Can't process response: #{e}"
127
158
  []
128
159
  end
129
160
 
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fulfil
4
+ class Error < StandardError; end
5
+
6
+ # The `Fulfil::HttpError` is raised whenever an API request returns a 400+ HTTP status code.
7
+ # See `Fulfil::ResponseHandler` for more information.
8
+ class HttpError < Error
9
+ attr_reader :metadata
10
+
11
+ def initialize(message, metadata = {})
12
+ @metadata = metadata
13
+ super(message)
14
+ end
15
+
16
+ class BadRequest < HttpError; end
17
+ class AuthorizationRequired < HttpError; end
18
+ class PaymentRequired < HttpError; end
19
+ class Forbidden < HttpError; end
20
+ class NotFound < HttpError; end
21
+ class MethodNotAllowed < HttpError; end
22
+ class NotAccepted < HttpError; end
23
+ class UnprocessableEntity < HttpError; end
24
+ class TooManyRequests < HttpError; end
25
+ class InternalServerError < HttpError; end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module Fulfil
2
+ class InteractiveReport
3
+ def initialize(client:, report:)
4
+ @client = client
5
+ @report = report
6
+ end
7
+
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
+
19
+ @client.interactive_report(
20
+ endpoint: report_url,
21
+ body: [body]
22
+ )
23
+ end
24
+
25
+ private
26
+
27
+ def report_url
28
+ "#{@report}/execute"
29
+ end
30
+
31
+ def serialize_date(date)
32
+ {
33
+ __class__: 'date',
34
+ year: date.year,
35
+ month: date.month,
36
+ day: date.day
37
+ }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,52 @@
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
+ return true unless @status_code >= 400
35
+
36
+ raise HTTP_ERROR_CODES.fetch(@status_code, Fulfil::HttpError).new(
37
+ response_body['error_description'],
38
+ {
39
+ body: @response.body,
40
+ headers: @response.headers,
41
+ status: @response.status
42
+ }
43
+ )
44
+ end
45
+
46
+ private
47
+
48
+ def response_body
49
+ @response_body ||= @response.parse
50
+ end
51
+ end
52
+ 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 { |group_key, kv_tuples|
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fulfil
4
- VERSION = '0.4.4'
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/fulfil.rb CHANGED
@@ -1,8 +1,12 @@
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'
7
+ require 'fulfil/interactive_report'
8
+ require 'fulfil/response_handler'
4
9
  require 'fulfil/response_parser'
5
10
 
6
11
  module Fulfil
7
- class Error < StandardError; end
8
12
  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.4
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Moore
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-05-20 00:00:00.000000000 Z
12
+ date: 2021-12-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: http
@@ -95,6 +95,20 @@ dependencies:
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: webmock
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
98
112
  description:
99
113
  email:
100
114
  - chris@knowndecimal.com
@@ -111,8 +125,11 @@ files:
111
125
  - Rakefile
112
126
  - lib/fulfil.rb
113
127
  - lib/fulfil/client.rb
128
+ - lib/fulfil/error.rb
129
+ - lib/fulfil/interactive_report.rb
114
130
  - lib/fulfil/model.rb
115
131
  - lib/fulfil/query.rb
132
+ - lib/fulfil/response_handler.rb
116
133
  - lib/fulfil/response_parser.rb
117
134
  - lib/fulfil/version.rb
118
135
  homepage: https://github.com/knowndecimal/fulfil
@@ -134,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
151
  - !ruby/object:Gem::Version
135
152
  version: '0'
136
153
  requirements: []
137
- rubygems_version: 3.1.4
154
+ rubygems_version: 3.1.6
138
155
  signing_key:
139
156
  specification_version: 4
140
157
  summary: Interact with the Fulfil.io API