fulfil_api 0.4.1 → 0.5.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: 2fd9d19ce17cb0e08c7c53cedf565ac90c16dd16bb12e027caa674a70e6e98f0
4
- data.tar.gz: c3d01131d48cafd5de51823fe8281709ed3c00992d1853cc672d6c40a2016ff1
3
+ metadata.gz: 22b6b63d1460a3a0d6b6c5ef3b33b018291d6919a27c32664c7649922a930852
4
+ data.tar.gz: 0a87ce490bca0026c5764ded0e887a3cdffd4cf0d9a3f13bcfd8e5a5a888cb43
5
5
  SHA512:
6
- metadata.gz: f8adecf2a1becece7421a498c0fe28f626c29273241ac9c7cd2a8494f9b352db790b988f766a472cd804ff0a620349ed2c4c039d0d85eb20138dd2dc7f2a0b73
7
- data.tar.gz: bf380b8c367f887cd024886fc582787220e36565715d6dcd2308b56759575548e6f80a10250faef50c5c56ae6d67981b7c7a6205e97d9d049f05dd6c09a0c8c0
6
+ metadata.gz: dedeecd3042e63045c2a5215d5bc6121c99a0863a65db953657cef068ff75f060271533d8ce2dd39a9b24ca94834b5b0b1282778ba6f0fb8d18efd419ecdff32
7
+ data.tar.gz: b39a10b911e3c9909cb7f21bfdf4cde91db64438eb6060a54700e4596ec4fe32961b691e49f4370211fdca0f8b343b0c63421ccfeb6d5959b6c05524ba415a94
data/README.md CHANGED
@@ -127,6 +127,47 @@ line_items = FulfilApi::Resource.set(model_name: "sale.line").where(["id", "in",
127
127
  line_items = FulfilApi::Resource.set(model_name: "sale.line").find_by(["sale.id", "=", 100])
128
128
  ```
129
129
 
130
+ ### Using the 3PL (TPL) Client
131
+
132
+ The gem also includes a client for Fulfil's [3PL Integration API](https://fulfil-3pl-integration-api.readme.io/reference/getting-started-with-your-api). This is a separate API that allows third-party logistics providers to interact with Fulfil on behalf of a merchant.
133
+
134
+ #### Configuration
135
+
136
+ Configure the 3PL client through the `tpl` option in the configuration block:
137
+
138
+ ```ruby
139
+ FulfilApi.configure do |config|
140
+ config.merchant_id = "the-id-of-the-merchant"
141
+
142
+ config.tpl = {
143
+ auth_token: ENV["FULFIL_3PL_AUTH_TOKEN"], # required
144
+ merchant_id: "a-different-merchant-id", # optional, falls back to config.merchant_id
145
+ api_version: "v1" # optional, defaults to "v1"
146
+ }
147
+ end
148
+ ```
149
+
150
+ #### Making Requests
151
+
152
+ The 3PL client is accessible via `FulfilApi.tpl_client` and supports the standard HTTP methods:
153
+
154
+ ```ruby
155
+ # GET request with optional URL parameters
156
+ FulfilApi.tpl_client.get("inbound-transfers", url_parameters: { page: 1, per_page: 25 })
157
+
158
+ # POST request with a request body
159
+ FulfilApi.tpl_client.post("inbound-transfers/receive.json", body: { tracking_number: "ABC123" })
160
+
161
+ # PUT request with a request body
162
+ FulfilApi.tpl_client.put("inbound-transfers/receive.json", body: { status: "received" })
163
+
164
+ # PATCH request with a request body
165
+ FulfilApi.tpl_client.patch("inbound-transfers/receive.json", body: { status: "received" })
166
+
167
+ ```
168
+
169
+ > **NOTE:** For the full list of available 3PL API endpoints, refer to the [Fulfil 3PL Integration API documentation](https://fulfil-3pl-integration-api.readme.io/reference/getting-started-with-your-api).
170
+
130
171
  ## Development
131
172
 
132
173
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -6,7 +6,7 @@ module FulfilApi
6
6
  # This model holds configuration settings and provides thread-safe access
7
7
  # to these settings.
8
8
  class Configuration
9
- attr_accessor :access_token, :api_version, :merchant_id, :request_options
9
+ attr_accessor :access_token, :api_version, :merchant_id, :request_options, :tpl
10
10
 
11
11
  DEFAULT_API_VERSION = "v2"
12
12
  DEFAULT_REQUEST_OPTIONS = { open_timeout: 1, read_timeout: 5, write_timeout: 5, timeout: 5 }.freeze
@@ -41,8 +41,49 @@ module FulfilApi
41
41
  .and_return(status: status, body: response.to_json, headers: { "Content-Type": "application/json" })
42
42
  end
43
43
 
44
+ # Stubs an HTTP request to the Fulfil 3PL API based on the provided parameters.
45
+ #
46
+ # @param [String, Symbol] method The HTTP method to be stubbed (e.g., :get, :post).
47
+ # @param [Hash] response The response body to return as a JSON object (default is {}).
48
+ # @param [Integer] status The HTTP status code to return (default is 200).
49
+ # @param [Hash] options Additional options for the request URL.
50
+ # @option options [String] :path The relative path to the 3PL endpoint
51
+ # (e.g., 'inbound-transfers/receive.json').
52
+ #
53
+ # @return [WebMock::RequestStub] The WebMock request stub object.
54
+ #
55
+ # @example Stub a GET request for the inbound transfers endpoint
56
+ # stub_fulfil_tpl_request(:get, path: "inbound-transfers", response: [{ id: 1 }])
57
+ #
58
+ # @example Stub a POST request for receiving an inbound transfer
59
+ # stub_fulfil_tpl_request(:post, path: "inbound-transfers/receive.json", response: { id: 123 })
60
+ #
61
+ # @example Stub all 3PL requests for a given method
62
+ # stub_fulfil_tpl_request(:get)
63
+ def stub_fulfil_tpl_request(method, response: {}, status: 200, **options)
64
+ stubbed_tpl_request_for(method, **options)
65
+ .and_return(status: status, body: response.to_json, headers: { "Content-Type": "application/json" })
66
+ end
67
+
44
68
  private
45
69
 
70
+ # Builds the WebMock request stub for the Fulfil 3PL API based on the provided method and options.
71
+ #
72
+ # @param [String, Symbol] method The HTTP method to be stubbed (e.g., :get, :post).
73
+ # @param [Hash] options Additional options for the request URL.
74
+ # @option options [String] :path The relative path to the 3PL endpoint
75
+ # (e.g., 'inbound-transfers/receive.json').
76
+ #
77
+ # @return [WebMock::RequestStub] The WebMock request stub object.
78
+ def stubbed_tpl_request_for(method, **options)
79
+ case options.transform_keys(&:to_sym)
80
+ in { path: }
81
+ stub_request(method.to_sym, %r{fulfil.io/services/3pl/v\d+/#{path}(.*)}i)
82
+ else
83
+ stub_request(method.to_sym, %r{fulfil.io/services/3pl/v\d+}i)
84
+ end
85
+ end
86
+
46
87
  # Builds the WebMock request stub for the Fulfil API based on the provided method and options.
47
88
  #
48
89
  # @param [String, Symbol] method The HTTP method to be stubbed (e.g., :get, :post).
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "faraday/net_http_persistent"
5
+
6
+ module FulfilApi
7
+ # The {FulfilApi::TplClient} allows making proxy requests to Fulfil's 3PL
8
+ # carrier API endpoint. It provides a simple interface for interacting with
9
+ # the 3PL supplier API using standard HTTP methods.
10
+ #
11
+ # @example Using the TPL client
12
+ # FulfilApi.tpl_client.get("inbound-transfers", url_parameters: { page: 1 })
13
+ # FulfilApi.tpl_client.post("inbound-transfers/receive.json", body: { tracking_number: "123" })
14
+ class TplClient
15
+ class ConfigurationError < FulfilApi::Error; end
16
+
17
+ DEFAULT_API_VERSION = "v1"
18
+
19
+ # @param configuration [FulfilApi::Configuration]
20
+ def initialize(configuration)
21
+ @configuration = configuration
22
+
23
+ tpl_config = configuration.tpl || {}
24
+
25
+ @auth_token = tpl_config[:auth_token].presence ||
26
+ raise(ConfigurationError,
27
+ "Please provide a 3PL authentication token via config.tpl = { auth_token: ... }")
28
+ @merchant_id = tpl_config[:merchant_id].presence || configuration.merchant_id.presence ||
29
+ raise(ConfigurationError, "Please provide a merchant ID")
30
+ @api_version = tpl_config[:api_version].presence || DEFAULT_API_VERSION
31
+ end
32
+
33
+ # Performs an HTTP GET request to a 3PL API endpoint.
34
+ #
35
+ # @param relative_path [String] The relative path to the endpoint.
36
+ # @param url_parameters [Hash, nil] The optional URL parameters for the API endpoint.
37
+ # @return [Array, Hash, String] The parsed response body.
38
+ def get(relative_path, url_parameters: nil)
39
+ request(:get, relative_path, url_parameters)
40
+ end
41
+
42
+ # Performs an HTTP PATCH request to a 3PL API endpoint.
43
+ #
44
+ # @param relative_path [String] The relative path to the endpoint.
45
+ # @param body [Array, Hash, nil] The request body for the PATCH HTTP request.
46
+ # @return [Array, Hash, String] The parsed response body.
47
+ def patch(relative_path, body: {})
48
+ request(:patch, relative_path, body)
49
+ end
50
+
51
+ # Performs an HTTP POST request to a 3PL API endpoint.
52
+ #
53
+ # @param relative_path [String] The relative path to the endpoint.
54
+ # @param body [Array, Hash, nil] The request body for the POST HTTP request.
55
+ # @return [Array, Hash, String] The parsed response body.
56
+ def post(relative_path, body: {})
57
+ request(:post, relative_path, body)
58
+ end
59
+
60
+ # Performs an HTTP PUT request to a 3PL API endpoint.
61
+ #
62
+ # @param relative_path [String] The relative path to the endpoint.
63
+ # @param body [Array, Hash, nil] The optional request body for the PUT HTTP request.
64
+ # @return [Array, Hash, String] The parsed response body.
65
+ def put(relative_path, body: nil)
66
+ return request(:put, relative_path) if body.nil?
67
+
68
+ request(:put, relative_path, body)
69
+ end
70
+
71
+ private
72
+
73
+ attr_reader :api_version, :auth_token, :configuration, :merchant_id
74
+
75
+ # @return [String] The absolute URL to the API base URL.
76
+ def api_endpoint
77
+ @api_endpoint ||= "https://#{merchant_id}.fulfil.io"
78
+ end
79
+
80
+ # @return [Faraday::Connection]
81
+ def connection
82
+ @connection ||= Faraday.new(
83
+ headers: request_headers,
84
+ url: api_endpoint,
85
+ request: configuration.request_options
86
+ ) do |connection|
87
+ connection.adapter :net_http_persistent
88
+
89
+ # Configuration of the request middleware
90
+ connection.request :json
91
+
92
+ # Configuration of the response middleware
93
+ connection.response :json
94
+ connection.response :raise_error
95
+ end
96
+ end
97
+
98
+ # @param relative_path [String] The relative path to the API endpoint.
99
+ # @return [String] The absolute path for the request to the API endpoint.
100
+ def expand_relative_path(relative_path)
101
+ path = relative_path.start_with?("/") ? relative_path[1..] : relative_path
102
+ "/services/3pl/#{api_version}/#{path}"
103
+ end
104
+
105
+ # @param exception [Faraday::Error] Any error raised by Faraday during the execution
106
+ # of the HTTP request to the API endpoint.
107
+ def handle_request_error(exception)
108
+ raise FulfilApi::Error.new(
109
+ exception.message,
110
+ details: {
111
+ response_body: exception.response_body,
112
+ response_headers: exception.response_headers,
113
+ response_status: exception.response_status
114
+ }
115
+ )
116
+ end
117
+
118
+ # @param method [Symbol, String] The HTTP verb for the HTTP request.
119
+ # @param relative_path [String] The relative path to the API endpoint.
120
+ # @return [Array, Hash, String] The parsed response body.
121
+ def request(method, relative_path, *args, **kwargs)
122
+ connection.send(method.to_sym, expand_relative_path(relative_path), *args, **kwargs).body
123
+ rescue Faraday::Error => e
124
+ handle_request_error(e)
125
+ end
126
+
127
+ # @return [Hash] The HTTP headers for any HTTP request to the 3PL API.
128
+ def request_headers
129
+ {
130
+ "Authorization" => "Bearer #{auth_token}",
131
+ "Content-Type" => "application/json"
132
+ }
133
+ end
134
+ end
135
+
136
+ # Builds an HTTP client to interact with Fulfil's 3PL API endpoint.
137
+ #
138
+ # @example
139
+ # FulfilApi.tpl_client.get("inbound-transfers")
140
+ # FulfilApi.tpl_client.post("inbound-transfers/receive.json", body: { tracking_number: "123" })
141
+ #
142
+ # @return [FulfilApi::TplClient]
143
+ def self.tpl_client
144
+ TplClient.new(FulfilApi.configuration)
145
+ end
146
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FulfilApi
4
- VERSION = "0.4.1"
4
+ VERSION = "0.5.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fulfil_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Vermaas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-11-19 00:00:00.000000000 Z
11
+ date: 2026-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -100,6 +100,7 @@ files:
100
100
  - lib/fulfil_api/resource/persistable.rb
101
101
  - lib/fulfil_api/resource/serializable.rb
102
102
  - lib/fulfil_api/test_helper.rb
103
+ - lib/fulfil_api/tpl_client.rb
103
104
  - lib/fulfil_api/version.rb
104
105
  - sig/fulfil_api.rbs
105
106
  homepage: https://www.github.com/codeturebv/fulfil_api