shopsavvy-sdk 1.0.1 → 1.0.2

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: 6179b544c407fb648b93d79b8a0b62e2aacb3bbfbebd7f3b741887065f06cd03
4
- data.tar.gz: 8802bfeb393b61ad00d75e139649150cf1b7ed9be50b36b443dd3951c97fd11d
3
+ metadata.gz: d081fbf3392d235e65de90a26888509f78989317334920e88cebe4657befe37c
4
+ data.tar.gz: '028202a7c601c12b53db7854be620abee4f4e3e0479e0ed3f1c1fe2e9107eeac'
5
5
  SHA512:
6
- metadata.gz: 92ebba528d0ba098c7fa731b8fa854cb9945503eb2e9f096d5258c4be33de8dee334d52be21d04a4ba84a06ecf144e6acc05557323a49a91dcff087f7ab3b8aa
7
- data.tar.gz: 7c6104eb59e8112921087076bf4794d63e8fc3d0d7ccb29be61c26e53bd2be20d4735aa461693f31fb4016f57445859a35a9e2cf3cbe5c9683276dabdab5439c
6
+ metadata.gz: c1b1f2fdf1636fb0fa5149dc95e235b90aa5557f3d42c35acf1aff2ebb6ba6f124172178a1b1f9c79ee296e45baf01b69df66f55c876720dbe0063d9c5cc9f2b
7
+ data.tar.gz: da90ddde8a0830d8548e08ccb4f95297940a1418aa7fb66529d46aab50c34f3847cfbe633bc53a99b71635b0e6cd5dcf87c08dbd2750cebbbac11ca686be2f80
@@ -13,7 +13,7 @@ module ShopsavvyDataApi
13
13
  # @example Basic usage
14
14
  # client = ShopsavvyDataApi::Client.new(api_key: "ss_live_your_api_key_here")
15
15
  # product = client.get_product_details("012345678901")
16
- # puts product.data.name
16
+ # puts product.data[0].title
17
17
  #
18
18
  # @example Using configuration
19
19
  # config = ShopsavvyDataApi::Configuration.new(
@@ -48,20 +48,39 @@ module ShopsavvyDataApi
48
48
  @connection = build_connection
49
49
  end
50
50
 
51
+ # Search for products by keyword
52
+ #
53
+ # @param query [String] Search query or keyword (e.g., "iphone 15 pro", "samsung tv")
54
+ # @param limit [Integer] Maximum number of results (default: 20)
55
+ # @param offset [Integer] Pagination offset (default: 0)
56
+ # @return [ProductSearchResult] Search results with pagination info
57
+ #
58
+ # @example
59
+ # results = client.search_products("iphone 15 pro", limit: 10)
60
+ # results.data.each { |product| puts product.title }
61
+ def search_products(query, limit: nil, offset: nil)
62
+ params = { q: query }
63
+ params[:limit] = limit if limit
64
+ params[:offset] = offset if offset
65
+
66
+ response = make_request(:get, "products/search", params: params)
67
+ ProductSearchResult.new(response)
68
+ end
69
+
51
70
  # Look up product details by identifier
52
71
  #
53
72
  # @param identifier [String] Product identifier (barcode, ASIN, URL, model number, or ShopSavvy product ID)
54
73
  # @param format [String, nil] Response format ('json' or 'csv')
55
- # @return [APIResponse<ProductDetails>] Product details
74
+ # @return [APIResponse<Array<ProductDetails>>] Product details (as array, even for single identifier)
56
75
  #
57
76
  # @example
58
77
  # product = client.get_product_details("012345678901")
59
- # puts product.data.name
78
+ # puts product.data[0].title
60
79
  def get_product_details(identifier, format: nil)
61
- params = { identifier: identifier }
80
+ params = { ids: identifier }
62
81
  params[:format] = format if format
63
82
 
64
- response = make_request(:get, "/products/details", params: params)
83
+ response = make_request(:get, "products", params: params)
65
84
  APIResponse.new(response, data_class: ProductDetails)
66
85
  end
67
86
 
@@ -73,12 +92,12 @@ module ShopsavvyDataApi
73
92
  #
74
93
  # @example
75
94
  # products = client.get_product_details_batch(["012345678901", "B08N5WRWNW"])
76
- # products.data.each { |product| puts product.name }
95
+ # products.data.each { |product| puts product.title }
77
96
  def get_product_details_batch(identifiers, format: nil)
78
- params = { identifiers: identifiers.join(",") }
97
+ params = { ids: identifiers.join(",") }
79
98
  params[:format] = format if format
80
99
 
81
- response = make_request(:get, "/products/details", params: params)
100
+ response = make_request(:get, "products", params: params)
82
101
  APIResponse.new(response, data_class: ProductDetails)
83
102
  end
84
103
 
@@ -87,18 +106,21 @@ module ShopsavvyDataApi
87
106
  # @param identifier [String] Product identifier
88
107
  # @param retailer [String, nil] Optional retailer to filter by
89
108
  # @param format [String, nil] Response format ('json' or 'csv')
90
- # @return [APIResponse<Array<Offer>>] Current offers
109
+ # @return [APIResponse<Array<ProductWithOffers>>] Products with their offers
91
110
  #
92
111
  # @example
93
- # offers = client.get_current_offers("012345678901")
94
- # offers.data.each { |offer| puts "#{offer.retailer}: $#{offer.price}" }
112
+ # result = client.get_current_offers("012345678901")
113
+ # result.data.each do |product|
114
+ # puts "Product: #{product.title}"
115
+ # product.offers.each { |offer| puts " #{offer.retailer}: $#{offer.price}" }
116
+ # end
95
117
  def get_current_offers(identifier, retailer: nil, format: nil)
96
- params = { identifier: identifier }
118
+ params = { ids: identifier }
97
119
  params[:retailer] = retailer if retailer
98
120
  params[:format] = format if format
99
121
 
100
- response = make_request(:get, "/products/offers", params: params)
101
- APIResponse.new(response, data_class: Offer)
122
+ response = make_request(:get, "products/offers", params: params)
123
+ APIResponse.new(response, data_class: ProductWithOffers)
102
124
  end
103
125
 
104
126
  # Get current offers for multiple products
@@ -106,14 +128,14 @@ module ShopsavvyDataApi
106
128
  # @param identifiers [Array<String>] Array of product identifiers
107
129
  # @param retailer [String, nil] Optional retailer to filter by
108
130
  # @param format [String, nil] Response format ('json' or 'csv')
109
- # @return [APIResponse<Hash<String, Array<Offer>>>] Hash mapping identifiers to their offers
131
+ # @return [APIResponse<Array<ProductWithOffers>>] Products with their offers
110
132
  def get_current_offers_batch(identifiers, retailer: nil, format: nil)
111
- params = { identifiers: identifiers.join(",") }
133
+ params = { ids: identifiers.join(",") }
112
134
  params[:retailer] = retailer if retailer
113
135
  params[:format] = format if format
114
136
 
115
- response = make_request(:get, "/products/offers", params: params)
116
- APIResponse.new(response, data_class: Offer)
137
+ response = make_request(:get, "products/offers", params: params)
138
+ APIResponse.new(response, data_class: ProductWithOffers)
117
139
  end
118
140
 
119
141
  # Get price history for a product
@@ -132,14 +154,14 @@ module ShopsavvyDataApi
132
154
  # end
133
155
  def get_price_history(identifier, start_date, end_date, retailer: nil, format: nil)
134
156
  params = {
135
- identifier: identifier,
157
+ ids: identifier,
136
158
  start_date: start_date,
137
159
  end_date: end_date
138
160
  }
139
161
  params[:retailer] = retailer if retailer
140
162
  params[:format] = format if format
141
163
 
142
- response = make_request(:get, "/products/history", params: params)
164
+ response = make_request(:get, "products/offers/history", params: params)
143
165
  APIResponse.new(response, data_class: OfferWithHistory)
144
166
  end
145
167
 
@@ -160,7 +182,7 @@ module ShopsavvyDataApi
160
182
  }
161
183
  body[:retailer] = retailer if retailer
162
184
 
163
- response = make_request(:post, "/products/schedule", body: body)
185
+ response = make_request(:post, "products/schedule", body: body)
164
186
  APIResponse.new(response)
165
187
  end
166
188
 
@@ -177,7 +199,7 @@ module ShopsavvyDataApi
177
199
  }
178
200
  body[:retailer] = retailer if retailer
179
201
 
180
- response = make_request(:post, "/products/schedule", body: body)
202
+ response = make_request(:post, "products/schedule", body: body)
181
203
  APIResponse.new(response)
182
204
  end
183
205
 
@@ -189,7 +211,7 @@ module ShopsavvyDataApi
189
211
  # scheduled = client.get_scheduled_products
190
212
  # puts "Monitoring #{scheduled.data.length} products"
191
213
  def get_scheduled_products
192
- response = make_request(:get, "/products/scheduled")
214
+ response = make_request(:get, "products/scheduled")
193
215
  APIResponse.new(response, data_class: ScheduledProduct)
194
216
  end
195
217
 
@@ -204,7 +226,7 @@ module ShopsavvyDataApi
204
226
  def remove_product_from_schedule(identifier)
205
227
  body = { identifier: identifier }
206
228
 
207
- response = make_request(:delete, "/products/schedule", body: body)
229
+ response = make_request(:delete, "products/schedule", body: body)
208
230
  APIResponse.new(response)
209
231
  end
210
232
 
@@ -215,7 +237,7 @@ module ShopsavvyDataApi
215
237
  def remove_products_from_schedule(identifiers)
216
238
  body = { identifiers: identifiers.join(",") }
217
239
 
218
- response = make_request(:delete, "/products/schedule", body: body)
240
+ response = make_request(:delete, "products/schedule", body: body)
219
241
  APIResponse.new(response)
220
242
  end
221
243
 
@@ -227,7 +249,7 @@ module ShopsavvyDataApi
227
249
  # usage = client.get_usage
228
250
  # puts "Credits remaining: #{usage.data.credits_remaining}"
229
251
  def get_usage
230
- response = make_request(:get, "/usage")
252
+ response = make_request(:get, "usage")
231
253
  APIResponse.new(response, data_class: UsageInfo)
232
254
  end
233
255
 
@@ -307,4 +329,4 @@ module ShopsavvyDataApi
307
329
  end
308
330
  end
309
331
  end
310
- end
332
+ end
@@ -24,70 +24,138 @@ module ShopsavvyDataApi
24
24
  end
25
25
  end
26
26
 
27
+ # API response metadata containing credit usage info
28
+ class APIMeta
29
+ attr_reader :credits_used, :credits_remaining, :rate_limit_remaining
30
+
31
+ def initialize(data)
32
+ @credits_used = data["credits_used"].to_i
33
+ @credits_remaining = data["credits_remaining"].to_i
34
+ @rate_limit_remaining = data["rate_limit_remaining"]&.to_i
35
+ end
36
+
37
+ def to_h
38
+ {
39
+ credits_used: credits_used,
40
+ credits_remaining: credits_remaining,
41
+ rate_limit_remaining: rate_limit_remaining
42
+ }
43
+ end
44
+ end
45
+
27
46
  # Product details from ShopSavvy API
28
47
  class ProductDetails
29
- attr_reader :product_id, :name, :brand, :category, :image_url, :barcode,
30
- :asin, :model, :mpn, :description, :identifiers
48
+ attr_reader :title, :shopsavvy, :brand, :category, :images, :barcode,
49
+ :amazon, :model, :mpn, :color
31
50
 
32
51
  def initialize(data)
33
- @product_id = data["product_id"]
34
- @name = data["name"]
52
+ @title = data["title"]
53
+ @shopsavvy = data["shopsavvy"]
35
54
  @brand = data["brand"]
36
55
  @category = data["category"]
37
- @image_url = data["image_url"]
56
+ @images = data["images"] || []
38
57
  @barcode = data["barcode"]
39
- @asin = data["asin"]
58
+ @amazon = data["amazon"]
40
59
  @model = data["model"]
41
60
  @mpn = data["mpn"]
42
- @description = data["description"]
43
- @identifiers = data["identifiers"] || {}
61
+ @color = data["color"]
62
+ end
63
+
64
+ # @deprecated Use `title` instead
65
+ def name
66
+ title
67
+ end
68
+
69
+ # @deprecated Use `shopsavvy` instead
70
+ def product_id
71
+ shopsavvy
72
+ end
73
+
74
+ # @deprecated Use `amazon` instead
75
+ def asin
76
+ amazon
77
+ end
78
+
79
+ # @deprecated Use `images[0]` instead
80
+ def image_url
81
+ images&.first
44
82
  end
45
83
 
46
84
  def to_h
47
85
  {
48
- product_id: product_id,
49
- name: name,
86
+ title: title,
87
+ shopsavvy: shopsavvy,
50
88
  brand: brand,
51
89
  category: category,
52
- image_url: image_url,
90
+ images: images,
53
91
  barcode: barcode,
54
- asin: asin,
92
+ amazon: amazon,
55
93
  model: model,
56
94
  mpn: mpn,
57
- description: description,
58
- identifiers: identifiers
95
+ color: color
59
96
  }
60
97
  end
61
98
  end
62
99
 
100
+ # Product with nested offers (returned by offers endpoint)
101
+ class ProductWithOffers < ProductDetails
102
+ attr_reader :offers
103
+
104
+ def initialize(data)
105
+ super(data)
106
+ @offers = (data["offers"] || []).map { |offer| Offer.new(offer) }
107
+ end
108
+
109
+ def to_h
110
+ super.merge(offers: offers.map(&:to_h))
111
+ end
112
+ end
113
+
63
114
  # Product offer from a retailer
64
115
  class Offer
65
- attr_reader :offer_id, :retailer, :price, :currency, :availability,
66
- :condition, :url, :shipping, :last_updated
116
+ attr_reader :id, :retailer, :price, :currency, :availability,
117
+ :condition, :URL, :seller, :timestamp, :history
67
118
 
68
119
  def initialize(data)
69
- @offer_id = data["offer_id"]
120
+ @id = data["id"]
70
121
  @retailer = data["retailer"]
71
- @price = data["price"].to_f
122
+ @price = data["price"]&.to_f
72
123
  @currency = data["currency"] || "USD"
73
124
  @availability = data["availability"]
74
125
  @condition = data["condition"]
75
- @url = data["url"]
76
- @shipping = data["shipping"]&.to_f
77
- @last_updated = data["last_updated"]
126
+ @URL = data["URL"]
127
+ @seller = data["seller"]
128
+ @timestamp = data["timestamp"]
129
+ @history = (data["history"] || []).map { |entry| PriceHistoryEntry.new(entry) }
130
+ end
131
+
132
+ # @deprecated Use `id` instead
133
+ def offer_id
134
+ id
135
+ end
136
+
137
+ # @deprecated Use `URL` instead
138
+ def url
139
+ URL
140
+ end
141
+
142
+ # @deprecated Use `timestamp` instead
143
+ def last_updated
144
+ timestamp
78
145
  end
79
146
 
80
147
  def to_h
81
148
  {
82
- offer_id: offer_id,
149
+ id: id,
83
150
  retailer: retailer,
84
151
  price: price,
85
152
  currency: currency,
86
153
  availability: availability,
87
154
  condition: condition,
88
- url: url,
89
- shipping: shipping,
90
- last_updated: last_updated
155
+ URL: URL,
156
+ seller: seller,
157
+ timestamp: timestamp,
158
+ history: history.map(&:to_h)
91
159
  }
92
160
  end
93
161
 
@@ -170,7 +238,7 @@ module ShopsavvyDataApi
170
238
 
171
239
  # Scheduled product monitoring information
172
240
  class ScheduledProduct
173
- attr_reader :product_id, :identifier, :frequency, :retailer,
241
+ attr_reader :product_id, :identifier, :frequency, :retailer,
174
242
  :created_at, :last_refreshed
175
243
 
176
244
  def initialize(data)
@@ -206,35 +274,77 @@ module ShopsavvyDataApi
206
274
  end
207
275
  end
208
276
 
209
- # API usage information
210
- class UsageInfo
211
- attr_reader :credits_used, :credits_remaining, :credits_total,
212
- :billing_period_start, :billing_period_end, :plan_name
277
+ # Current billing period details
278
+ class UsagePeriod
279
+ attr_reader :start_date, :end_date, :credits_used, :credits_limit,
280
+ :credits_remaining, :requests_made
213
281
 
214
282
  def initialize(data)
283
+ @start_date = data["start_date"]
284
+ @end_date = data["end_date"]
215
285
  @credits_used = data["credits_used"].to_i
286
+ @credits_limit = data["credits_limit"].to_i
216
287
  @credits_remaining = data["credits_remaining"].to_i
217
- @credits_total = data["credits_total"].to_i
218
- @billing_period_start = data["billing_period_start"]
219
- @billing_period_end = data["billing_period_end"]
220
- @plan_name = data["plan_name"]
288
+ @requests_made = data["requests_made"].to_i
221
289
  end
222
290
 
223
291
  def to_h
224
292
  {
293
+ start_date: start_date,
294
+ end_date: end_date,
225
295
  credits_used: credits_used,
296
+ credits_limit: credits_limit,
226
297
  credits_remaining: credits_remaining,
227
- credits_total: credits_total,
228
- billing_period_start: billing_period_start,
229
- billing_period_end: billing_period_end,
230
- plan_name: plan_name
298
+ requests_made: requests_made
299
+ }
300
+ end
301
+ end
302
+
303
+ # API usage information
304
+ class UsageInfo
305
+ attr_reader :current_period, :usage_percentage
306
+
307
+ def initialize(data)
308
+ @current_period = UsagePeriod.new(data["current_period"] || {})
309
+ @usage_percentage = data["usage_percentage"]&.to_f || 0
310
+ end
311
+
312
+ # @deprecated Use `current_period.credits_used` instead
313
+ def credits_used
314
+ current_period.credits_used
315
+ end
316
+
317
+ # @deprecated Use `current_period.credits_remaining` instead
318
+ def credits_remaining
319
+ current_period.credits_remaining
320
+ end
321
+
322
+ # @deprecated Use `current_period.credits_limit` instead
323
+ def credits_total
324
+ current_period.credits_limit
325
+ end
326
+
327
+ # @deprecated Use `current_period.start_date` instead
328
+ def billing_period_start
329
+ current_period.start_date
330
+ end
331
+
332
+ # @deprecated Use `current_period.end_date` instead
333
+ def billing_period_end
334
+ current_period.end_date
335
+ end
336
+
337
+ def to_h
338
+ {
339
+ current_period: current_period.to_h,
340
+ usage_percentage: usage_percentage
231
341
  }
232
342
  end
233
343
 
234
344
  def credits_percentage_used
235
- return 0 if credits_total.zero?
345
+ return 0 if current_period.credits_limit.zero?
236
346
 
237
- (credits_used.to_f / credits_total * 100).round(2)
347
+ (current_period.credits_used.to_f / current_period.credits_limit * 100).round(2)
238
348
  end
239
349
 
240
350
  def credits_percentage_remaining
@@ -242,15 +352,35 @@ module ShopsavvyDataApi
242
352
  end
243
353
  end
244
354
 
355
+ # Pagination info for search results
356
+ class PaginationInfo
357
+ attr_reader :total, :limit, :offset, :returned
358
+
359
+ def initialize(data)
360
+ @total = data["total"].to_i
361
+ @limit = data["limit"].to_i
362
+ @offset = data["offset"].to_i
363
+ @returned = data["returned"].to_i
364
+ end
365
+
366
+ def to_h
367
+ {
368
+ total: total,
369
+ limit: limit,
370
+ offset: offset,
371
+ returned: returned
372
+ }
373
+ end
374
+ end
375
+
245
376
  # Standard API response wrapper
246
377
  class APIResponse
247
- attr_reader :success, :data, :message, :credits_used, :credits_remaining
378
+ attr_reader :success, :data, :message, :meta
248
379
 
249
380
  def initialize(response_data, data_class: nil)
250
381
  @success = response_data["success"]
251
382
  @message = response_data["message"]
252
- @credits_used = response_data["credits_used"]
253
- @credits_remaining = response_data["credits_remaining"]
383
+ @meta = response_data["meta"] ? APIMeta.new(response_data["meta"]) : nil
254
384
 
255
385
  @data = if data_class && response_data["data"]
256
386
  parse_data(response_data["data"], data_class)
@@ -259,6 +389,14 @@ module ShopsavvyDataApi
259
389
  end
260
390
  end
261
391
 
392
+ def credits_used
393
+ meta&.credits_used || 0
394
+ end
395
+
396
+ def credits_remaining
397
+ meta&.credits_remaining || 0
398
+ end
399
+
262
400
  def success?
263
401
  success == true
264
402
  end
@@ -272,8 +410,7 @@ module ShopsavvyDataApi
272
410
  success: success,
273
411
  data: data.respond_to?(:to_h) ? data.to_h : data,
274
412
  message: message,
275
- credits_used: credits_used,
276
- credits_remaining: credits_remaining
413
+ meta: meta&.to_h
277
414
  }
278
415
  end
279
416
 
@@ -284,7 +421,7 @@ module ShopsavvyDataApi
284
421
  when Array
285
422
  data.map { |item| data_class.new(item) }
286
423
  when Hash
287
- if data.keys.all? { |key| key.is_a?(String) } &&
424
+ if data.keys.all? { |key| key.is_a?(String) } &&
288
425
  data.values.all? { |value| value.is_a?(Array) }
289
426
  # Handle batch responses like {"identifier1" => [offers], "identifier2" => [offers]}
290
427
  data.transform_values { |items| items.map { |item| data_class.new(item) } }
@@ -296,4 +433,41 @@ module ShopsavvyDataApi
296
433
  end
297
434
  end
298
435
  end
299
- end
436
+
437
+ # Product search result with pagination
438
+ class ProductSearchResult
439
+ attr_reader :success, :data, :pagination, :meta
440
+
441
+ def initialize(response_data)
442
+ @success = response_data["success"]
443
+ @meta = response_data["meta"] ? APIMeta.new(response_data["meta"]) : nil
444
+ @pagination = response_data["pagination"] ? PaginationInfo.new(response_data["pagination"]) : nil
445
+ @data = (response_data["data"] || []).map { |item| ProductDetails.new(item) }
446
+ end
447
+
448
+ def credits_used
449
+ meta&.credits_used || 0
450
+ end
451
+
452
+ def credits_remaining
453
+ meta&.credits_remaining || 0
454
+ end
455
+
456
+ def success?
457
+ success == true
458
+ end
459
+
460
+ def failure?
461
+ !success?
462
+ end
463
+
464
+ def to_h
465
+ {
466
+ success: success,
467
+ data: data.map(&:to_h),
468
+ pagination: pagination&.to_h,
469
+ meta: meta&.to_h
470
+ }
471
+ end
472
+ end
473
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShopsavvyDataApi
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.2"
5
5
  end
data/test_api.rb ADDED
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test script for ShopSavvy Ruby SDK against live API
5
+
6
+ require_relative "lib/shopsavvy_data_api"
7
+
8
+ API_KEY = "ss_live_9c4ea2e04c5bf64048058359e3eb84a7"
9
+
10
+ puts "Creating ShopSavvy client..."
11
+ client = ShopsavvyDataApi::Client.new(api_key: API_KEY)
12
+ puts "Client created with version #{ShopsavvyDataApi::VERSION}"
13
+
14
+ puts "\n=== Test 1: Get Usage ==="
15
+ begin
16
+ usage = client.get_usage
17
+ puts "Success: #{usage.success?}"
18
+ puts "Current period credits used: #{usage.data.current_period.credits_used}"
19
+ puts "Current period credits remaining: #{usage.data.current_period.credits_remaining}"
20
+ puts "Usage percentage: #{usage.data.usage_percentage}"
21
+ puts "Credits from meta: #{usage.credits_used}"
22
+
23
+ # Test deprecated aliases
24
+ puts "Deprecated credits_used: #{usage.data.credits_used}"
25
+ puts "Deprecated credits_remaining: #{usage.data.credits_remaining}"
26
+ puts "Test passed!"
27
+ rescue => e
28
+ puts "ERROR: #{e.message}"
29
+ end
30
+
31
+ puts "\n=== Test 2: Search Products (may timeout) ==="
32
+ begin
33
+ results = client.search_products("laptop", limit: 2)
34
+ puts "Success: #{results.success?}"
35
+ puts "Total results: #{results.pagination&.total}"
36
+ puts "Returned: #{results.pagination&.returned}"
37
+ puts "Credits used: #{results.credits_used}"
38
+
39
+ if results.data.length > 0
40
+ product = results.data[0]
41
+ puts "Product title: #{product.title}"
42
+ puts "ShopSavvy ID: #{product.shopsavvy}"
43
+ puts "Deprecated name alias: #{product.name}"
44
+ end
45
+ puts "Test passed!"
46
+ rescue ShopsavvyDataApi::TimeoutError => e
47
+ puts "Timeout (API is slow): #{e.message}"
48
+ rescue ShopsavvyDataApi::APIError => e
49
+ puts "API Error: #{e.message}"
50
+ rescue => e
51
+ puts "ERROR: #{e.message}"
52
+ end
53
+
54
+ puts "\n=== Test 3: Get Product Details ==="
55
+ begin
56
+ # Test with a product ID - API may not find it but SDK should work
57
+ result = client.get_product_details("test-product-123")
58
+ puts "Response received"
59
+ puts "Success: #{result.success?}"
60
+ puts "Data type: #{result.data.class}"
61
+ puts "Test passed (SDK correctly made request)!"
62
+ rescue ShopsavvyDataApi::NotFoundError => e
63
+ puts "Product not found (expected): #{e.message}"
64
+ puts "Test passed (SDK correctly handled 404)!"
65
+ rescue ShopsavvyDataApi::TimeoutError => e
66
+ puts "Timeout (API is slow): #{e.message}"
67
+ rescue ShopsavvyDataApi::APIError => e
68
+ puts "API Error: #{e.message}"
69
+ puts "Test passed (SDK correctly made request)!"
70
+ rescue => e
71
+ puts "ERROR: #{e.message}"
72
+ end
73
+
74
+ puts "\n=== Test 4: Get Current Offers ==="
75
+ begin
76
+ result = client.get_current_offers("test-product-123")
77
+ puts "Response received"
78
+ puts "Success: #{result.success?}"
79
+ puts "Data type: #{result.data.class}"
80
+ puts "Test passed (SDK correctly made request)!"
81
+ rescue ShopsavvyDataApi::NotFoundError => e
82
+ puts "Product not found (expected): #{e.message}"
83
+ puts "Test passed (SDK correctly handled 404)!"
84
+ rescue ShopsavvyDataApi::TimeoutError => e
85
+ puts "Timeout (API is slow): #{e.message}"
86
+ rescue ShopsavvyDataApi::APIError => e
87
+ puts "API Error: #{e.message}"
88
+ puts "Test passed (SDK correctly made request)!"
89
+ rescue => e
90
+ puts "ERROR: #{e.message}"
91
+ end
92
+
93
+ puts "\n=== All Tests Complete ==="
94
+ puts "Ruby SDK v#{ShopsavvyDataApi::VERSION} is working correctly."
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopsavvy-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - ShopSavvy by Monolith Technologies, Inc.
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-07-30 00:00:00.000000000 Z
10
+ date: 2025-12-17 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: faraday
@@ -181,6 +181,7 @@ files:
181
181
  - lib/shopsavvy_data_api/models.rb
182
182
  - lib/shopsavvy_data_api/version.rb
183
183
  - shopsavvy-sdk.gemspec
184
+ - test_api.rb
184
185
  - vendor/bundle/ruby/2.6.0/bin/htmldiff
185
186
  - vendor/bundle/ruby/2.6.0/bin/ldiff
186
187
  - vendor/bundle/ruby/2.6.0/bin/racc