Skip to content

Commit

Permalink
Fix: Pagination redesign (#287)
Browse files Browse the repository at this point in the history
* Do not convert to EasyPost objects on client code

* Update Address and Batch services

* Update services: BetaRate, BetaReferralCustomer, Billing, CarrierAccount and CarrierMetadata

* Update another batch of services

* Refactor another round of services

* Update all remaining services

* Class not required to make requests via client

* Lint

* More lints

* CHANGELOG

* Merge previous filters to parameters

* Lower coverage to 90%
  • Loading branch information
dcaballeroc authored Nov 30, 2023
1 parent 3753698 commit 2176bd1
Show file tree
Hide file tree
Showing 46 changed files with 408 additions and 143 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Next release

- Removed `with_carbon_offset` parameter from `create`, `buy` and `regenerate_rates` methods in the shipment service since now EasyPost offers carbon neutral shipments by default for free
- Fixes a bug where the original filtering criteria of `all` calls wasn't passed along to `get_next_page` calls. Now, these are persisted via `_params` key on response objects

## v5.3.0 (2023-10-11)

Expand Down
2 changes: 1 addition & 1 deletion examples
4 changes: 1 addition & 3 deletions lib/easypost/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,13 @@ def initialize(api_key:, read_timeout: 60, open_timeout: 30, api_base: 'https://
#
# @param method [Symbol] the HTTP Verb (get, method, put, post, etc.)
# @param endpoint [String] URI path of the resource
# @param cls [Class] the class to deserialize to
# @param body [Object] (nil) object to be dumped to JSON
# @param api_version [String] the version of API to hit
# @raise [EasyPost::Error] if the response has a non-2xx status code
# @return [Hash] JSON object parsed from the response body
def make_request(
method,
endpoint,
cls = EasyPost::Models::EasyPostObject,
body = nil,
api_version = EasyPost::InternalUtilities::Constants::API_VERSION
)
Expand All @@ -89,7 +87,7 @@ def make_request(
potential_error = EasyPost::Errors::ApiError.handle_api_error(response)
raise potential_error unless potential_error.nil?

EasyPost::InternalUtilities::Json.convert_json_to_object(response.body, cls)
EasyPost::InternalUtilities::Json.parse_json(response.body)
end

# Subscribe a request hook
Expand Down
2 changes: 2 additions & 0 deletions lib/easypost/models/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ def []=(key, value)

def add_properties(values)
values.each do |key, _|
next if key == EasyPost::InternalUtilities::Constants::FILTERS_KEY

define_singleton_method(key) { handle_value(@values[key]) } # getter
define_singleton_method("#{key}=") { |v| @values[key] = handle_value(v) } # setter
end
Expand Down
29 changes: 22 additions & 7 deletions lib/easypost/services/address.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,49 @@ def create(params = {})
wrapped_params[:verify_strict] = params[:verify_strict]
end

@client.make_request(:post, 'addresses', MODEL_CLASS, params)
response = @client.make_request(:post, 'addresses', params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Create and verify an Address in one call.
def create_and_verify(params = {})
wrapped_params = {}
wrapped_params[:address] = params

@client.make_request(:post, 'addresses/create_and_verify', MODEL_CLASS, wrapped_params).address
response = @client.make_request(:post, 'addresses/create_and_verify', wrapped_params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS).address
end

# Verify an Address.
def verify(id)
@client.make_request(:get, "addresses/#{id}/verify", MODEL_CLASS).address
response = @client.make_request(:get, "addresses/#{id}/verify")

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS).address
end

# Retrieve an Address.
def retrieve(id)
@client.make_request(:get, "addresses/#{id}", MODEL_CLASS)
response = @client.make_request(:get, "addresses/#{id}")

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Retrieve all Addresses.
def all(filters = {})
@client.make_request(:get, 'addresses', MODEL_CLASS, filters)
def all(params = {})
filters = { key: 'addresses' }

get_all_helper('addresses', MODEL_CLASS, params, filters)
end

# Get the next page of addresses.
def get_next_page(collection, page_size = nil)
get_next_page_helper(collection, collection.addresses, 'addresses', MODEL_CLASS, page_size)
raise EasyPost::Errors::EndOfPaginationError.new unless more_pages?(collection)

params = { before_id: collection.addresses.last.id }
params[:page_size] = page_size unless page_size.nil?

all(params)
end
end
4 changes: 3 additions & 1 deletion lib/easypost/services/api_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
class EasyPost::Services::ApiKey < EasyPost::Services::Service
# Retrieve a list of all ApiKey objects.
def all
@client.make_request(:get, 'api_keys', EasyPost::Models::ApiKey)
response = @client.make_request(:get, 'api_keys')

EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::ApiKey)
end

# Retrieve a list of ApiKey objects (works for the authenticated user or a child user).
Expand Down
23 changes: 10 additions & 13 deletions lib/easypost/services/base.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require_relative '../internal_utilities'

# The base class for all services in the library.
class EasyPost::Services::Service
def initialize(client)
Expand All @@ -8,20 +10,15 @@ def initialize(client)

protected

# Get next page of an object collection
def get_next_page_helper(collection, current_page_items, endpoint, cls, page_size = nil)
has_more = collection.has_more || false
unless !has_more || current_page_items.nil? || current_page_items.empty?
params = {}
params[:before_id] = current_page_items.last.id
unless page_size.nil?
params[:page_size] = page_size
end
def get_all_helper(endpoint, cls, params, filters = nil)
response = @client.make_request(:get, endpoint, params)

response[EasyPost::InternalUtilities::Constants::FILTERS_KEY] = filters unless filters.nil?

@client.make_request(:get, endpoint, cls, params)
end
EasyPost::InternalUtilities::Json.convert_json_to_object(response, cls)
end

# issue with getting the next page
raise EasyPost::Errors::EndOfPaginationError.new
def more_pages?(collection)
collection&.has_more
end
end
34 changes: 25 additions & 9 deletions lib/easypost/services/batch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,64 @@ class EasyPost::Services::Batch < EasyPost::Services::Service
# Create a Batch.
def create(params = {})
wrapped_params = { batch: params }
response = @client.make_request(:post, 'batches', wrapped_params)

@client.make_request(:post, 'batches', MODEL_CLASS, wrapped_params)
EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Create and buy a batch in one call.
def create_and_buy(params = {})
wrapped_params = { batch: params }
response = @client.make_request(:post, 'batches/create_and_buy', wrapped_params)

@client.make_request(:post, 'batches/create_and_buy', MODEL_CLASS, wrapped_params)
EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

def all(params = {})
@client.make_request(:get, 'batches', EasyPost::Models::ApiKey, params)
filters = { key: 'batches' }

get_all_helper('batches', MODEL_CLASS, params, filters)
end

# Retrieve a Batch
def retrieve(id)
@client.make_request(:get, "batches/#{id}", MODEL_CLASS)
response = @client.make_request(:get, "batches/#{id}")

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Buy a Batch.
def buy(id, params = {})
@client.make_request(:post, "batches/#{id}/buy", MODEL_CLASS, params)
response = @client.make_request(:post, "batches/#{id}/buy", params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Convert the label format of a Batch.
def label(id, params = {})
@client.make_request(:post, "batches/#{id}/label", MODEL_CLASS, params)
response = @client.make_request(:post, "batches/#{id}/label", params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Remove Shipments from a Batch.
def remove_shipments(id, params = {})
@client.make_request(:post, "batches/#{id}/remove_shipments", MODEL_CLASS, params)
response = @client.make_request(:post, "batches/#{id}/remove_shipments", params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Add Shipments to a Batch.
def add_shipments(id, params = {})
@client.make_request(:post, "batches/#{id}/add_shipments", MODEL_CLASS, params)
response = @client.make_request(:post, "batches/#{id}/add_shipments", params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Create a ScanForm for a Batch.
def create_scan_form(id, params = {})
@client.make_request(:post, "batches/#{id}/scan_form", MODEL_CLASS, params)
response = @client.make_request(:post, "batches/#{id}/scan_form", params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end
end
3 changes: 2 additions & 1 deletion lib/easypost/services/beta_rate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ def retrieve_stateless_rates(params = {})
wrapped_params = {
shipment: params,
}
response = @client.make_request(:post, 'rates', wrapped_params, 'beta')

@client.make_request(:post, 'rates', EasyPost::Models::Rate, wrapped_params, 'beta').rates
EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::Rate).rates
end
end
14 changes: 9 additions & 5 deletions lib/easypost/services/beta_referral_customer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ def add_payment_method(stripe_customer_id, payment_method_reference, priority =
priority: priority.downcase,
},
}
@client.make_request(
response = @client.make_request(
:post,
'referral_customers/payment_method',
EasyPost::Models::EasyPostObject,
wrapped_params,
'beta',
)

EasyPost::InternalUtilities::Json.convert_json_to_object(response)
end

# Refund a ReferralCustomer Customer's wallet by a specified amount. Refund will be issued to the user's original payment method.
Expand All @@ -25,8 +26,9 @@ def refund_by_amount(amount)
params = {
refund_amount: amount,
}
@client.make_request(:post, 'referral_customers/refunds', EasyPost::Models::EasyPostObject, params, 'beta')
# noinspection RubyMismatchedReturnType
response = @client.make_request(:post, 'referral_customers/refunds', params, 'beta')

EasyPost::InternalUtilities::Json.convert_json_to_object(response)
end

# Refund a ReferralCustomer Customer's wallet for a specified payment log entry. Refund will be issued to the user's original payment method.
Expand All @@ -35,6 +37,8 @@ def refund_by_payment_log(payment_log_id)
params = {
payment_log_id: payment_log_id,
}
@client.make_request(:post, 'referral_customers/refunds', EasyPost::Models::EasyPostObject, params, 'beta')
response = @client.make_request(:post, 'referral_customers/refunds', params, 'beta')

EasyPost::InternalUtilities::Json.convert_json_to_object(response)
end
end
7 changes: 4 additions & 3 deletions lib/easypost/services/billing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def fund_wallet(amount, priority = 'primary')
payment_id = payment_info[1]

wrapped_params = { amount: amount }
@client.make_request(:post, "#{endpoint}/#{payment_id}/charges", EasyPost::Models::EasyPostObject, wrapped_params)
@client.make_request(:post, "#{endpoint}/#{payment_id}/charges", wrapped_params)

# Return true if succeeds, an error will be thrown if it fails
true
Expand All @@ -65,11 +65,12 @@ def delete_payment_method(priority)
# Retrieve all payment methods.
def retrieve_payment_methods
response = @client.make_request(:get, '/v2/payment_methods')
payment_methods = EasyPost::InternalUtilities::Json.convert_json_to_object(response)

if response['id'].nil?
if payment_methods['id'].nil?
raise EasyPost::Errors::InvalidObjectError.new(EasyPost::Constants::NO_PAYMENT_METHODS)
end

response
payment_methods
end
end
13 changes: 9 additions & 4 deletions lib/easypost/services/carrier_account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,29 @@ def create(params = {})
else
'carrier_accounts'
end
response = @client.make_request(:post, create_url, wrapped_params)

@client.make_request(:post, create_url, MODEL_CLASS, wrapped_params)
EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Retrieve a carrier account
def retrieve(id)
@client.make_request(:get, "carrier_accounts/#{id}", MODEL_CLASS)
response = @client.make_request(:get, "carrier_accounts/#{id}")

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Retrieve all carrier accounts
def all(params = {})
@client.make_request(:get, 'carrier_accounts', MODEL_CLASS, params)
get_all_helper('carrier_accounts', MODEL_CLASS, params)
end

# Update a carrier account
def update(id, params = {})
wrapped_params = { carrier_account: params }
@client.make_request(:put, "carrier_accounts/#{id}", MODEL_CLASS, wrapped_params)
response = @client.make_request(:put, "carrier_accounts/#{id}", wrapped_params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Delete a carrier account
Expand Down
4 changes: 2 additions & 2 deletions lib/easypost/services/carrier_metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ class EasyPost::Services::CarrierMetadata < EasyPost::Services::Service
# Retrieve metadata for carrier(s).
def retrieve(carriers = [], types = [])
path = '/metadata/carriers?'

params = {}

if carriers.length.positive?
Expand All @@ -16,7 +15,8 @@ def retrieve(carriers = [], types = [])
end

path += URI.encode_www_form(params)
response = @client.make_request(:get, path, params)

@client.make_request(:get, path, EasyPost::Models::EasyPostObject, params).carriers
EasyPost::InternalUtilities::Json.convert_json_to_object(response).carriers
end
end
4 changes: 3 additions & 1 deletion lib/easypost/services/carrier_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ class EasyPost::Services::CarrierType < EasyPost::Services::Service

# Retrieve all carrier types
def all
@client.make_request(:get, 'carrier_types', MODEL_CLASS)
response = @client.make_request(:get, 'carrier_types')

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end
end
7 changes: 5 additions & 2 deletions lib/easypost/services/customs_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ class EasyPost::Services::CustomsInfo < EasyPost::Services::Service
# Create a CustomsInfo object
def create(params)
wrapped_params = { customs_info: params }
response = @client.make_request(:post, 'customs_infos', wrapped_params)

@client.make_request(:post, 'customs_infos', MODEL_CLASS, wrapped_params)
EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Retrieve a CustomsInfo object
def retrieve(id)
@client.make_request(:get, "customs_infos/#{id}", MODEL_CLASS)
response = @client.make_request(:get, "customs_infos/#{id}")

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end
end
8 changes: 6 additions & 2 deletions lib/easypost/services/customs_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ class EasyPost::Services::CustomsItem < EasyPost::Services::Service

# Create a CustomsItem object
def create(params)
@client.make_request(:post, 'customs_items', MODEL_CLASS, params)
response = @client.make_request(:post, 'customs_items', params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end

# Retrieve a CustomsItem object
def retrieve(id)
@client.make_request(:get, "customs_items/#{id}", MODEL_CLASS)
response = @client.make_request(:get, "customs_items/#{id}")

EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS)
end
end
Loading

0 comments on commit 2176bd1

Please sign in to comment.