mirror of
https://github.com/stripe/stripe-ruby.git
synced 2025-12-04 00:01:22 -05:00
Add support for Search API (#992)
This commit is contained in:
parent
8d379bf1a7
commit
ba38dc9d24
@ -24,6 +24,7 @@ require "stripe/api_operations/list"
|
||||
require "stripe/api_operations/nested_resource"
|
||||
require "stripe/api_operations/request"
|
||||
require "stripe/api_operations/save"
|
||||
require "stripe/api_operations/search"
|
||||
|
||||
# API resource support classes
|
||||
require "stripe/errors"
|
||||
@ -35,6 +36,7 @@ require "stripe/stripe_client"
|
||||
require "stripe/stripe_object"
|
||||
require "stripe/stripe_response"
|
||||
require "stripe/list_object"
|
||||
require "stripe/search_result_object"
|
||||
require "stripe/error_object"
|
||||
require "stripe/api_resource"
|
||||
require "stripe/singleton_api_resource"
|
||||
|
||||
19
lib/stripe/api_operations/search.rb
Normal file
19
lib/stripe/api_operations/search.rb
Normal file
@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Stripe
|
||||
module APIOperations
|
||||
module Search
|
||||
def _search(search_url, filters = {}, opts = {})
|
||||
opts = Util.normalize_opts(opts)
|
||||
|
||||
resp, opts = execute_resource_request(:get, search_url, filters, opts)
|
||||
obj = SearchResultObject.construct_from(resp.data, opts)
|
||||
|
||||
# set filters so that we can fetch the same limit and query
|
||||
# when accessing the next page
|
||||
obj.filters = filters.dup
|
||||
obj
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -9,6 +9,7 @@ module Stripe
|
||||
{
|
||||
# data structures
|
||||
ListObject::OBJECT_NAME => ListObject,
|
||||
SearchResultObject::OBJECT_NAME => SearchResultObject,
|
||||
|
||||
# business objects
|
||||
Account::OBJECT_NAME => Account,
|
||||
|
||||
86
lib/stripe/search_result_object.rb
Normal file
86
lib/stripe/search_result_object.rb
Normal file
@ -0,0 +1,86 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Stripe
|
||||
class SearchResultObject < StripeObject
|
||||
include Enumerable
|
||||
include Stripe::APIOperations::Search
|
||||
include Stripe::APIOperations::Request
|
||||
|
||||
OBJECT_NAME = "search_result"
|
||||
|
||||
# This accessor allows a `SearchResultObject` to inherit various filters
|
||||
# that were given to a predecessor. This allows for things like consistent
|
||||
# limits, expansions, and predicates as a user pages through resources.
|
||||
attr_accessor :filters
|
||||
|
||||
# An empty search result object. This is returned from +next+ when we know
|
||||
# that there isn't a next page in order to replicate the behavior of the API
|
||||
# when it attempts to return a page beyond the last.
|
||||
def self.empty_search_result(opts = {})
|
||||
SearchResultObject.construct_from({ data: [] }, opts)
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
self.filters = {}
|
||||
end
|
||||
|
||||
def [](key)
|
||||
case key
|
||||
when String, Symbol
|
||||
super
|
||||
else
|
||||
raise ArgumentError,
|
||||
"You tried to access the #{key.inspect} index, but " \
|
||||
"SearchResultObject types only support String keys. " \
|
||||
"(HINT: Search calls return an object with a 'data' (which is " \
|
||||
"the data array). You likely want to call #data[#{key.inspect}])"
|
||||
end
|
||||
end
|
||||
|
||||
# Iterates through each resource in the page represented by the current
|
||||
# `SearchListObject`.
|
||||
#
|
||||
# Note that this method makes no effort to fetch a new page when it gets to
|
||||
# the end of the current page's resources. See also +auto_paging_each+.
|
||||
def each(&blk)
|
||||
data.each(&blk)
|
||||
end
|
||||
|
||||
# Returns true if the page object contains no elements.
|
||||
def empty?
|
||||
data.empty?
|
||||
end
|
||||
|
||||
# Iterates through each resource in all pages, making additional fetches to
|
||||
# the API as necessary.
|
||||
#
|
||||
# Note that this method will make as many API calls as necessary to fetch
|
||||
# all resources. For more granular control, please see +each+ and
|
||||
# +next_search_result_page+.
|
||||
def auto_paging_each(&blk)
|
||||
return enum_for(:auto_paging_each) unless block_given?
|
||||
|
||||
page = self
|
||||
|
||||
loop do
|
||||
page.each(&blk)
|
||||
page = page.next_search_result_page
|
||||
|
||||
break if page.empty?
|
||||
end
|
||||
end
|
||||
|
||||
# Fetches the next page in the resource list (if there is one).
|
||||
#
|
||||
# This method will try to respect the limit of the current page. If none
|
||||
# was given, the default limit will be fetched again.
|
||||
def next_search_result_page(params = {}, opts = {})
|
||||
return self.class.empty_search_result(opts) unless has_more
|
||||
|
||||
params = filters.merge(page: next_page).merge(params)
|
||||
|
||||
_search(url, params, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
132
test/stripe/search_result_object_test.rb
Normal file
132
test/stripe/search_result_object_test.rb
Normal file
@ -0,0 +1,132 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require ::File.expand_path("../test_helper", __dir__)
|
||||
|
||||
module Stripe
|
||||
class SearchResultObjectTest < Test::Unit::TestCase
|
||||
should "provide .empty_list" do
|
||||
list = Stripe::SearchResultObject.empty_search_result
|
||||
assert list.empty?
|
||||
end
|
||||
|
||||
should "provide #count via enumerable" do
|
||||
list = Stripe::SearchResultObject.construct_from(data: [{ object: "charge" }])
|
||||
assert_equal 1, list.count
|
||||
end
|
||||
|
||||
should "provide #each" do
|
||||
arr = [
|
||||
{ id: 1 },
|
||||
{ id: 2 },
|
||||
{ id: 3 },
|
||||
]
|
||||
expected = Util.convert_to_stripe_object(arr, {})
|
||||
list = Stripe::SearchResultObject.construct_from(data: arr)
|
||||
assert_equal expected, list.each.to_a
|
||||
end
|
||||
|
||||
should "provide #auto_paging_each that supports forward pagination" do
|
||||
arr = [
|
||||
{ id: 1 },
|
||||
{ id: 2 },
|
||||
{ id: 3 },
|
||||
{ id: 4 },
|
||||
{ id: 5 },
|
||||
{ id: 6 },
|
||||
]
|
||||
|
||||
list = TestSearchResultObject.construct_from({ data: [{ id: 1 }],
|
||||
has_more: true,
|
||||
next_page: "next_page_token_1",
|
||||
url: "/things", })
|
||||
list.filters = { limit: 3 }
|
||||
|
||||
# The test will start with the synthetic search result object above, and uses the
|
||||
# 'next_page' token to fetch two more pages. The second page indicates
|
||||
# that there are no more elements by setting `has_more` to `false`, and
|
||||
# iteration stops.
|
||||
stub_request(:get, "#{Stripe.api_base}/things")
|
||||
.with(query: { limit: 3, page: "next_page_token_1" })
|
||||
.to_return(body: JSON.generate(data: [{ id: 2 }, { id: 3 }, { id: 4 }], has_more: true, url: "/things", next_page: "next_page_token_2"))
|
||||
stub_request(:get, "#{Stripe.api_base}/things")
|
||||
.with(query: { limit: 3, page: "next_page_token_2" })
|
||||
.to_return(body: JSON.generate(data: [{ id: 5 }, { id: 6 }], has_more: false, url: "/things", next_page: nil))
|
||||
|
||||
assert_equal arr, list.auto_paging_each.to_a.map(&:to_hash)
|
||||
end
|
||||
|
||||
should "provide #auto_paging_each that responds to a block" do
|
||||
arr = [
|
||||
{ id: 1 },
|
||||
{ id: 2 },
|
||||
{ id: 3 },
|
||||
]
|
||||
expected = Util.convert_to_stripe_object(arr, {})
|
||||
|
||||
list = TestSearchResultObject.construct_from(data: [{ id: 1 }],
|
||||
has_more: true,
|
||||
next_page: "next_page_token_1",
|
||||
url: "/things")
|
||||
|
||||
stub_request(:get, "#{Stripe.api_base}/things")
|
||||
.with(query: { page: "next_page_token_1" })
|
||||
.to_return(body: JSON.generate(data: [{ id: 2 }, { id: 3 }], has_more: false))
|
||||
|
||||
actual = []
|
||||
list.auto_paging_each do |obj|
|
||||
actual << obj
|
||||
end
|
||||
|
||||
assert_equal expected, actual
|
||||
end
|
||||
|
||||
should "provide #empty?" do
|
||||
list = Stripe::SearchResultObject.construct_from(data: [])
|
||||
assert list.empty?
|
||||
list = Stripe::SearchResultObject.construct_from(data: [{}])
|
||||
refute list.empty?
|
||||
end
|
||||
|
||||
#
|
||||
# next_page
|
||||
#
|
||||
|
||||
should "fetch a next page through #next_page" do
|
||||
list = TestSearchResultObject.construct_from(data: [{ id: 1 }],
|
||||
has_more: true,
|
||||
next_page: "next_page_token_1",
|
||||
url: "/things")
|
||||
stub_request(:get, "#{Stripe.api_base}/things")
|
||||
.with(query: { page: "next_page_token_1" })
|
||||
.to_return(body: JSON.generate(data: [{ id: 2 }], has_more: false))
|
||||
next_list = list.next_search_result_page
|
||||
refute next_list.empty?
|
||||
assert_equal [{ id: 2 }], next_list.auto_paging_each.to_a.map(&:to_hash)
|
||||
end
|
||||
|
||||
should "fetch a next page through #next_page and respect limit" do
|
||||
list = TestSearchResultObject.construct_from(data: [{ id: 1 }],
|
||||
has_more: true,
|
||||
next_page: "next_page_token_1",
|
||||
url: "/things")
|
||||
list.filters = { limit: 3 }
|
||||
stub_request(:get, "#{Stripe.api_base}/things")
|
||||
.with(query: { "limit": 3, page: "next_page_token_1" })
|
||||
.to_return(body: JSON.generate(data: [{ id: 2 }], has_more: false))
|
||||
next_list = list.next_search_result_page
|
||||
assert_equal({ limit: 3, page: "next_page_token_1" }, next_list.filters)
|
||||
end
|
||||
|
||||
should "fetch an empty page through #next_page" do
|
||||
list = TestSearchResultObject.construct_from(data: [{ id: 1 }],
|
||||
has_more: false,
|
||||
url: "/things")
|
||||
next_list = list.next_search_result_page
|
||||
assert_equal Stripe::SearchResultObject.empty_search_result, next_list
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# A helper class with a URL that allows us to try out pagination.
|
||||
class TestSearchResultObject < Stripe::SearchResultObject
|
||||
end
|
||||
Loading…
x
Reference in New Issue
Block a user