parseapi 1.6.0 → 1.8.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 +4 -4
- data/README.md +76 -15
- data/lib/parseapi/client.rb +81 -25
- data/lib/parseapi/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3bd44c95f15db2d0248e3b9afebf94580ef381cd5df317b668d92bd6ba8e5f3a
|
|
4
|
+
data.tar.gz: 51258c88b79a3c61ce685fbc5c4fa4606adc86ee1a64a22574f9cd41a690b418
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9bd2d3e93c4c1f5aa2668146a4af0ad4a18379a483ca77578971980d11dbc64be2942c3a50522723f4ff2492cd23b523fb077769b38c6f4b77aa1f1670a8c71c
|
|
7
|
+
data.tar.gz: 1d06c9bf515839953e35f563bc808a177fde1bdac02a4f7a095b90a6d34b2013dfdbec32d4b7b95d0f046cdb514094b7fc370e6c2581bfe1fefe31713be7de1d
|
data/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Get a key at [parseapi.com](https://parseapi.com). The client also reads `PARSEA
|
|
|
13
13
|
|
|
14
14
|
## API versions
|
|
15
15
|
|
|
16
|
-
Version 1.
|
|
16
|
+
Version 1.7.0 explicitly selects the API contract supported by this SDK. It sends `Parse-Version: 2.0.0` on every lookup so responses match the API contract supported by the package. Your key and the team's saved default stay the same.
|
|
17
17
|
|
|
18
18
|
Upgrade the dependency in staging, review the [release notes](https://parseapi.com/docs/releases), and test the application before deploying the same code and dependency version to production. Commit your dependency lockfile so the tested package travels with your deployment. Future major SDK upgrades can select a newer API contract.
|
|
19
19
|
|
|
@@ -53,9 +53,9 @@ parse.ip('8.8.8.8')
|
|
|
53
53
|
parse.ip_self
|
|
54
54
|
parse.email('hello@gmail.com')
|
|
55
55
|
parse.vat('DE136695976')
|
|
56
|
-
parse.
|
|
57
|
-
parse.
|
|
58
|
-
parse.
|
|
56
|
+
parse.bank('DE89370400440532013000')
|
|
57
|
+
parse.card('424242')
|
|
58
|
+
parse.provider('1881018208')
|
|
59
59
|
parse.phone('+14155552671')
|
|
60
60
|
parse.carrier('+14155552671')
|
|
61
61
|
parse.caller('+14155552671')
|
|
@@ -107,16 +107,18 @@ parse.mx('example.com')
|
|
|
107
107
|
parse.dns('example.com')
|
|
108
108
|
parse.dns('_dmarc.example.com', type: 'TXT')
|
|
109
109
|
parse.useragent(ua_string)
|
|
110
|
-
parse.
|
|
111
|
-
parse.
|
|
112
|
-
parse.
|
|
110
|
+
parse.vehicle('1HGCM82633A004352')
|
|
111
|
+
parse.industry('541511')
|
|
112
|
+
parse.industry_search('coffee shop', limit: 5)
|
|
113
113
|
parse.tariff('8471.30.01.00', origin: 'CN', deep: true)
|
|
114
114
|
parse.tariff_search('sunglasses')
|
|
115
115
|
parse.emoji('rocket')
|
|
116
116
|
parse.emoji_search('fire')
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
The existing NAICS lookup and search methods remain available as compatibility names for Industry.
|
|
120
|
+
|
|
121
|
+
Industry paid deep records include classification `deep.exclusions`, each with a description and linked codes. Generic exclusions can have no linked codes. Omitted or null exclusions in older responses remain unknown. Search results also include `match`: the matched `field` (`name`, `term` or `naics`) and `text`, plus `corrections` with `from` and `to` tokens for typo fallback. Corrections are empty for exact, plural and prefix matches. Direct code lookups omit `match`. Older responses may omit it.
|
|
120
122
|
|
|
121
123
|
Each lookup returns a plain hash with string keys. Related lookups are separate calls, such as `country_states('US')`. Reading the result makes no further requests. New response fields and `nil` values are preserved.
|
|
122
124
|
|
|
@@ -219,6 +221,12 @@ parse.weather(40.7128, -74.006, deep: true, date: '2026-08-15')
|
|
|
219
221
|
|
|
220
222
|
Tariff starts with the general schedule line. Paid deep adds units and the special and other schedule columns. An optional origin then resolves country-specific measures. The three calls below show those successive choices. Without origin, schedule detail is still returned and origin-dependent fields are null. A null effective rate is not a zero rate.
|
|
221
223
|
|
|
224
|
+
Tariff lookup and search accept an optional `edition` fingerprint and `date` (`YYYY-MM-DD`). The edition pins exact immutable source bytes. A date is accepted only when verified source coverage exists. An edition without a date returns undated schedule context (`date: null`). Default requests use today. Paid detail exposes an open-string `reason` when `effective_rate` is null, including `incomplete_coverage`. A null rate never means zero. Explicit selections fail with `tariff_selection_mismatch` if an older server ignores the requested scope.
|
|
225
|
+
|
|
226
|
+
Origin means where the goods originate, not where they ship from. The effective rate covers matched stored schedule measures only. It is not complete duty or landed cost.
|
|
227
|
+
|
|
228
|
+
Codes contain 4, 6, 8 or 10 ASCII digits; dots and whitespace are optional. Search returns up to 20 description matches with parent `lineage` so a result named "Other" has context. Search is not product classification. In deep, `measures: null` means origin-dependent measures were not resolved. `measures: []` means the resolved lookup found none.
|
|
229
|
+
|
|
222
230
|
```ruby
|
|
223
231
|
parse.tariff('8471.30.01.00')
|
|
224
232
|
parse.tariff('8471.30.01.00', deep: true)
|
|
@@ -229,6 +237,32 @@ Address search uses context from the form: prefer postal, or city and state. An
|
|
|
229
237
|
|
|
230
238
|
HLR reports status at the last check. `live` means assigned and `connected` means reachable at that check. Cached results may be returned. Null means unconfirmed. Deep diagnostics stay within the same metered lookup.
|
|
231
239
|
|
|
240
|
+
Bank returns core `checks` for input, country, length, structure, checksum and national rules, plus an `issues` list. States are `passed`, `failed`, `not_checked` or `not_supported`. Unsupported national checking is not a failure. `valid` covers the implemented format and checksum rules, not account existence, ownership or payment reachability. Directory names and BICs may be null independently. Older responses may omit `checks` and `issues`, and future states and issue codes remain strings. Pass the original input unchanged so the API can report invalid characters. Deep `account` remains the BBAN remainder.
|
|
241
|
+
|
|
242
|
+
Bank inputs use `POST /bank` JSON bodies, keeping IBAN and account values out of request URLs. Pass original strings; the server owns normalization and validation. Avoid logging request bodies. IBAN deep can include `directory` with the immutable `edition`, resolved `country` and actual `match` grain (`bank`, `branch`, `prefix` or `none`); it is absent if no directory lookup ran. A match does not prove complete country coverage or payment reachability.
|
|
243
|
+
|
|
244
|
+
Use country requirements to build supported input fields. US ACH has an explicit helper with no deep option. It checks the routing format/ABA checksum and account-field syntax; `account_checksum` is `not_supported`. It preserves account characters and leading zeros. A nullable bank name is routing-directory identity, not account existence, ownership or ACH eligibility. The examples below are synthetic test inputs, not payment instructions.
|
|
245
|
+
|
|
246
|
+
```ruby
|
|
247
|
+
parse.bank_requirements('US', format: 'us_ach')
|
|
248
|
+
parse.bank_us_ach(routing: '011000015', account: '0001234567')
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Provider lookup
|
|
252
|
+
|
|
253
|
+
```ruby
|
|
254
|
+
provider = parse.provider('1881018208')
|
|
255
|
+
profile = parse.provider('1881018208', deep: true)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Pass the original NPI as a string. `valid` checks its format and checksum; `registered` means a match in the stored NPPES snapshot. `active` reflects recorded NPI deactivation, not licensure. `excluded` is an NPI-only OIG LEIE match; `false` is not a complete exclusion clearance. These directory facts do not verify credentials, current practice contact or payment eligibility.
|
|
259
|
+
|
|
260
|
+
Invalid input returns `valid: false` with unknown provider fields. A checksum-valid number missing from the snapshot returns `registered: false`; unavailable storage remains an API error. Preserve `null` as unknown.
|
|
261
|
+
|
|
262
|
+
The default pooled lookup includes provider identity, specialty and practice contact where held. Paid `deep` adds `deactivated_at`, `medicare`, `opt_out` and `enrollments` from stored source files, with no separate check meter or live verification. `enrollments: null` means unavailable; `[]` means no enrollment rows are returned. The API omits unrequested `deep` and returns `{}` when requested on Free.
|
|
263
|
+
|
|
264
|
+
Paid Deep also returns `taxonomies` in published order, with taxonomy code, specialty label, primary flag and provider-reported license number/state, plus `enumerated_at`, `updated_at` and `reactivated_at` record dates. Reported licenses are not verified licenses. Null lists mean unavailable; empty lists mean the edition contains no entries. Core `sources` is available on every plan: NPPES, LEIE, PECOS and opt-out each have nullable edition metadata (`edition`, `published_at`, `through`, `imported_at`). Provider record dates are separate from source publication and completed import dates. Older responses may omit these additions. Edition details remain null until a verified source is served.
|
|
265
|
+
|
|
232
266
|
## Deep
|
|
233
267
|
|
|
234
268
|
Choose enrichment for the question you need answered.
|
|
@@ -239,10 +273,11 @@ Choose enrichment for the question you need answered.
|
|
|
239
273
|
| Domain | Registration dates, registrar, status and DNSSEC, included with a paid plan. Use `dns` for DNS records and `mx` for mail routing. |
|
|
240
274
|
| Email | A metered mailbox check with deliverability, catch-all, status, reason and address hints, using included email checks or enabled on-demand usage. |
|
|
241
275
|
| VAT | A metered registry check where supported, using included VAT checks or enabled on-demand usage. |
|
|
242
|
-
| Phone, Time, Date, Currency, Language, Emoji,
|
|
276
|
+
| Phone, Time, Date, Currency, Language, Emoji, Bank, Point | Optional detail in the same pooled request on every plan. |
|
|
243
277
|
| Country, State, District, City, Postal | The place profile on paid plans, including demographic and tax facts where held. |
|
|
244
|
-
| Name,
|
|
245
|
-
|
|
|
278
|
+
| Name, Industry | Name evidence or the industry definition profile on paid plans. |
|
|
279
|
+
| NPI | Deactivation date, Medicare enrollment, opt-out and enrollment rows from stored sources on paid plans. Exclusion evidence stays core. |
|
|
280
|
+
| Vehicle, Tariff, Company | The complete product detail bag on paid plans. |
|
|
246
281
|
| Weather | Specialist current measurements and the existing forecast, alert, air and history bag on paid plans. |
|
|
247
282
|
| Carrier, HLR | Optional diagnostic detail within the same metered core unit, including Free allowance units. No second gate or additional check. |
|
|
248
283
|
|
|
@@ -261,7 +296,7 @@ ip.dig('deep', 'datacenter') # true, false, or nil
|
|
|
261
296
|
|
|
262
297
|
## Errors
|
|
263
298
|
|
|
264
|
-
Every non-2xx response raises `ParseAPI::Error` with `status`, `code`, `docs`, and `request_id
|
|
299
|
+
Every non-2xx response raises `ParseAPI::Error` with `status`, `code`, `docs`, and `request_id`, plus nullable `retry_after` header metadata. Branch on `code`.
|
|
265
300
|
|
|
266
301
|
```ruby
|
|
267
302
|
begin
|
|
@@ -286,11 +321,13 @@ Ordinary lookups retry network errors and HTTP 429, 500, 502, 503, and 504 up to
|
|
|
286
321
|
|
|
287
322
|
Pass `retries: 0` to make every lookup a single attempt. An explicit count such as `retries: 2` applies to every lookup, including paid ones. A retried request can count toward usage even when the first response was lost. Omit `retries` or pass `nil` to use the defaults above.
|
|
288
323
|
|
|
324
|
+
Automatic retries honor numeric and HTTP-date `Retry-After` values up to five seconds. A longer server wait returns the original API error immediately, with the raw header in `retry_after`, so the application can schedule a later attempt. Missing or invalid headers use ordinary backoff.
|
|
325
|
+
|
|
289
326
|
Reuse one client for successive lookups. Call `parse.close` to release its connection when finished. A later lookup opens a new connection. Use a separate client in each concurrent thread.
|
|
290
327
|
|
|
291
328
|
Network failures raise native Ruby exceptions. An invalid JSON response raises `JSON::ParserError`.
|
|
292
329
|
|
|
293
|
-
For testing or instrumentation, pass a callable as `transport:`.
|
|
330
|
+
For testing or instrumentation, pass a callable as `transport:`. For GET it receives the URL and request-header hash. For Bank POST it also receives a third `"POST"` argument and fourth serialized JSON body argument. Accept optional method/body parameters when supplying a custom transport. It returns `[status, lowercase_response_headers, body]`. Custom transports should use the supplied headers and keep redirect following disabled.
|
|
294
331
|
|
|
295
332
|
Requires Ruby 3.0 or later. Standard library only, zero dependencies.
|
|
296
333
|
|
|
@@ -298,12 +335,34 @@ Requires Ruby 3.0 or later. Standard library only, zero dependencies.
|
|
|
298
335
|
|
|
299
336
|
Full field reference for every endpoint: [parseapi.com/docs](https://parseapi.com/docs)
|
|
300
337
|
|
|
301
|
-
|
|
338
|
+
## Card
|
|
302
339
|
|
|
340
|
+
Send 2–11 leading digits as a string. Core returns `bin`, `brand`, `brand_name`
|
|
341
|
+
and a CDN SVG `logo`. Brand detection uses reviewed network rules independently
|
|
342
|
+
of issuer records. Unknown or ambiguous prefixes return null brand fields and a
|
|
343
|
+
generic logo; a known network without reviewed artwork also uses the generic logo.
|
|
344
|
+
|
|
345
|
+
Optional Deep adds `prefix`, `issuer`, `country`, `type` and `prepaid`, included
|
|
346
|
+
in the same pooled request on every plan. Six or more digits enable directory
|
|
347
|
+
matching. Fewer digits return all-null Deep fields. Compare `deep.prefix` with
|
|
348
|
+
`bin`: equal is an exact recorded match; shorter is broader; null is no match.
|
|
349
|
+
The longest row wins, including null fields. `prepaid: null` means unknown, not
|
|
350
|
+
false. This is partial reference data, not card validity or payment acceptance.
|
|
351
|
+
|
|
352
|
+
```ruby
|
|
353
|
+
card = parse.card("51")
|
|
354
|
+
puts card["logo"]
|
|
355
|
+
details = parse.card("43737400", deep: true)
|
|
356
|
+
issuer = details["deep"]["issuer"]
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Leading zeros are preserved. Only ASCII spaces, tabs, CR, LF and hyphens are
|
|
360
|
+
removed; raw input is limited to 64 characters. Invalid prefixes are rejected
|
|
361
|
+
before dispatch, accepted input is forwarded unchanged. Never send a full card number.
|
|
303
362
|
|
|
304
363
|
## Optional detail
|
|
305
364
|
|
|
306
|
-
The default response answers the common task. Ask for `deep` when you need more detail about that same result. Core fields stay equal. City,
|
|
365
|
+
The default response answers the common task. Ask for `deep` when you need more detail about that same result. Core fields stay equal. City, Industry and Emoji searches put detail inside each result. Postal nearby and distance put metropolitan detail beside the entity it describes. Time conversion keeps target detail in `to.deep`; only the source has `deep.next_dst`.
|
|
307
366
|
|
|
308
367
|
```ruby
|
|
309
368
|
basic = parse.time('America/New_York')
|
|
@@ -324,3 +383,5 @@ Pass a public hostname without a scheme, path, port or IP address. Stack returns
|
|
|
324
383
|
Successful checks may be reused for up to 24 hours. `pretty` optionally formats the wire JSON. Stack uses your plan's request allowance and API version 2.0.0 selected by this client.
|
|
325
384
|
|
|
326
385
|
Stack defaults to a 35-second transport timeout so a first scan has time to finish. Other lookups retain their 10-second default. An explicit client timeout takes precedence.
|
|
386
|
+
|
|
387
|
+
Vehicle lookups use `vin` as the input and response field. Existing VIN methods remain available for compatibility.
|
data/lib/parseapi/client.rb
CHANGED
|
@@ -6,14 +6,15 @@ require 'time'
|
|
|
6
6
|
module ParseAPI
|
|
7
7
|
# Every non-2xx response from the API. Branch on +code+, never on the message.
|
|
8
8
|
class Error < StandardError
|
|
9
|
-
attr_reader :status, :code, :docs, :request_id
|
|
9
|
+
attr_reader :status, :code, :docs, :request_id, :retry_after
|
|
10
10
|
|
|
11
|
-
def initialize(status:, code:, message:, docs: nil, request_id: nil)
|
|
11
|
+
def initialize(status:, code:, message:, docs: nil, request_id: nil, retry_after: nil)
|
|
12
12
|
super(message)
|
|
13
13
|
@status = status
|
|
14
14
|
@code = code
|
|
15
15
|
@docs = docs
|
|
16
16
|
@request_id = request_id
|
|
17
|
+
@retry_after = retry_after
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
|
|
@@ -173,16 +174,39 @@ module ParseAPI
|
|
|
173
174
|
get("/iban/#{seg(iban)}", country: country, deep: deep)
|
|
174
175
|
end
|
|
175
176
|
|
|
176
|
-
# Look up a 6-11 digit card prefix, preserving leading zeros.
|
|
177
177
|
def bin(bin, deep: false)
|
|
178
178
|
get("/bin/#{seg(bin)}", deep: deep)
|
|
179
179
|
end
|
|
180
180
|
|
|
181
|
-
|
|
182
181
|
def npi(npi, deep: false, lang: nil)
|
|
183
182
|
get("/npi/#{seg(npi)}", deep: deep, lang: lang)
|
|
184
183
|
end
|
|
185
184
|
|
|
185
|
+
def bank(iban, country: nil, deep: false)
|
|
186
|
+
get("/bank", {}, {}, { iban: iban, country: country, deep: deep }.reject { |_key, value| value.nil? })
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Look up a 2-11 digit card prefix, preserving leading zeros.
|
|
190
|
+
def card(bin, deep: false)
|
|
191
|
+
raise ArgumentError, 'parseapi: Card requires a 2-11 digit prefix string.' unless bin.is_a?(String) && bin.length <= 64 && /\A[0-9]{2,11}\z/.match?(bin.delete(" \t\r\n-"))
|
|
192
|
+
get("/card/#{seg(bin)}", deep: deep)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# US routing/account syntax only; not account or ACH eligibility verification.
|
|
197
|
+
def bank_us_ach(routing:, account:)
|
|
198
|
+
get('/bank', {}, {}, { format: 'us_ach', country: 'US', routing: routing, account: account })
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Describe accepted fields and check scope, not directory completeness.
|
|
202
|
+
def bank_requirements(country, format: nil)
|
|
203
|
+
get('/bank/requirements', country: country, format: format)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def provider(npi, deep: false, lang: nil)
|
|
207
|
+
get("/provider/#{seg(npi)}", deep: deep, lang: lang)
|
|
208
|
+
end
|
|
209
|
+
|
|
186
210
|
# Parse a phone number and its formats. Pass country for national numbers when needed. Deep
|
|
187
211
|
# adds numbering-plan geography on every plan. Carrier, caller, and HLR are separate metered lookups.
|
|
188
212
|
def phone(number, country: nil, deep: false)
|
|
@@ -240,30 +264,43 @@ module ParseAPI
|
|
|
240
264
|
get('/useragent', { deep: deep }, { 'User-Agent' => ua })
|
|
241
265
|
end
|
|
242
266
|
|
|
267
|
+
def vehicle(vin, deep: false)
|
|
268
|
+
get("/vehicle/#{seg(vin)}", deep: deep)
|
|
269
|
+
end
|
|
270
|
+
|
|
243
271
|
def vin(vin, deep: false)
|
|
244
272
|
get("/vin/#{seg(vin)}", deep: deep)
|
|
245
273
|
end
|
|
246
274
|
|
|
247
275
|
# US NAICS 2022 definition and hierarchy.
|
|
276
|
+
# Compatibility names for Industry.
|
|
248
277
|
def naics(code, deep: false)
|
|
249
|
-
|
|
278
|
+
industry(code, deep: deep)
|
|
250
279
|
end
|
|
251
280
|
|
|
252
|
-
# Keyword search. Limit defaults to 10 and accepts 1-50.
|
|
253
281
|
def naics_search(query, limit: nil, deep: false)
|
|
254
|
-
|
|
282
|
+
industry_search(query, limit: limit, deep: deep)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def industry(code, deep: false)
|
|
286
|
+
get("/industry/#{seg(code)}", deep: deep)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Keyword search. Limit defaults to 10 and accepts 1-50.
|
|
290
|
+
def industry_search(query, limit: nil, deep: false)
|
|
291
|
+
get('/industry', q: query, limit: limit, deep: deep)
|
|
255
292
|
end
|
|
256
293
|
|
|
257
294
|
# Look up the general US duty schedule line. Paid deep adds units and the special and other
|
|
258
295
|
# schedule columns. Add origin with deep to resolve country-specific measures. Without origin,
|
|
259
296
|
# schedule detail remains available and origin-dependent fields are null. A null effective rate
|
|
260
297
|
# is not a zero rate.
|
|
261
|
-
def tariff(code, deep: false, origin: nil)
|
|
262
|
-
get("/tariff/#{seg(code)}", deep: deep, origin: origin)
|
|
298
|
+
def tariff(code, deep: false, origin: nil, edition: nil, date: nil)
|
|
299
|
+
tariff_selection(get("/tariff/#{seg(code)}", deep: deep, origin: origin, edition: edition, date: date), edition, date)
|
|
263
300
|
end
|
|
264
301
|
|
|
265
|
-
def tariff_search(query)
|
|
266
|
-
get('/tariff', q: query)
|
|
302
|
+
def tariff_search(query, edition: nil, date: nil)
|
|
303
|
+
tariff_selection(get('/tariff', q: query, edition: edition, date: date), edition, date)
|
|
267
304
|
end
|
|
268
305
|
|
|
269
306
|
def currency(code, deep: false, lang: nil)
|
|
@@ -381,21 +418,34 @@ module ParseAPI
|
|
|
381
418
|
targets.join(',')
|
|
382
419
|
end
|
|
383
420
|
|
|
421
|
+
def tariff_selection(result, edition, date)
|
|
422
|
+
if (!edition.nil? || !date.nil?) && (!result['edition'].is_a?(String) || !/\A[a-f0-9]{64}\z/.match?(result['edition']) || (!edition.nil? && result['edition'] != edition) || result['date'] != date)
|
|
423
|
+
raise Error.new(status: 0, code: 'tariff_selection_mismatch', message: 'Tariff response did not confirm the requested edition/date. The server may not support this selection.')
|
|
424
|
+
end
|
|
425
|
+
result
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
|
|
384
429
|
def seg(value)
|
|
385
430
|
URI.encode_www_form_component(value.to_s).gsub('+', '%20')
|
|
386
431
|
end
|
|
387
432
|
|
|
388
|
-
def get(path, params = {}, headers = {})
|
|
433
|
+
def get(path, params = {}, headers = {}, json = nil)
|
|
389
434
|
retries = retries_for(path, params)
|
|
390
435
|
query = params.reject { |_name, value| value.nil? || value == false }
|
|
391
436
|
uri = @base_url.dup
|
|
392
437
|
uri.path = path
|
|
393
438
|
uri.query = URI.encode_www_form(query) unless query.empty?
|
|
394
439
|
|
|
440
|
+
encoded = json.nil? ? nil : JSON.generate(json)
|
|
395
441
|
attempt = 0
|
|
396
442
|
loop do
|
|
397
443
|
begin
|
|
398
|
-
status, response_headers, body =
|
|
444
|
+
status, response_headers, body = if encoded.nil?
|
|
445
|
+
execute(uri, request_headers(headers))
|
|
446
|
+
else
|
|
447
|
+
execute(uri, request_headers(headers.merge('Content-Type' => 'application/json')), 'POST', encoded)
|
|
448
|
+
end
|
|
399
449
|
rescue *NETWORK_ERRORS
|
|
400
450
|
raise if attempt >= retries
|
|
401
451
|
|
|
@@ -406,13 +456,14 @@ module ParseAPI
|
|
|
406
456
|
|
|
407
457
|
return JSON.parse(body) if (200..299).cover?(status)
|
|
408
458
|
|
|
409
|
-
|
|
410
|
-
|
|
459
|
+
retry_after = response_headers['retry-after']
|
|
460
|
+
if RETRY_STATUS.include?(status) && attempt < retries && (wait = retry_delay(attempt, retry_after))
|
|
461
|
+
sleep(wait)
|
|
411
462
|
attempt += 1
|
|
412
463
|
next
|
|
413
464
|
end
|
|
414
465
|
|
|
415
|
-
raise build_error(status, body)
|
|
466
|
+
raise build_error(status, body, retry_after)
|
|
416
467
|
end
|
|
417
468
|
end
|
|
418
469
|
|
|
@@ -421,12 +472,13 @@ module ParseAPI
|
|
|
421
472
|
end
|
|
422
473
|
|
|
423
474
|
# Returns [status, headers_hash, body_string]. Overridden in tests.
|
|
424
|
-
def execute(uri, headers)
|
|
425
|
-
return @transport.call(uri.to_s, headers) if @transport
|
|
475
|
+
def execute(uri, headers, method = 'GET', body = nil)
|
|
476
|
+
return (method == 'GET' ? @transport.call(uri.to_s, headers) : @transport.call(uri.to_s, headers, method, body)) if @transport
|
|
426
477
|
|
|
427
478
|
timeout = !@timeout_explicit && uri.path.start_with?('/stack/') ? 35 : @timeout
|
|
428
479
|
http = connection(timeout)
|
|
429
|
-
request = Net::HTTP::Get.new(uri.request_uri)
|
|
480
|
+
request = (method == 'POST' ? Net::HTTP::Post : Net::HTTP::Get).new(uri.request_uri)
|
|
481
|
+
request.body = body unless body.nil?
|
|
430
482
|
headers.each { |name, value| request[name] = value }
|
|
431
483
|
response = http.request(request)
|
|
432
484
|
header_hash = {}
|
|
@@ -451,15 +503,18 @@ module ParseAPI
|
|
|
451
503
|
|
|
452
504
|
def retry_delay(attempt, retry_after)
|
|
453
505
|
if retry_after
|
|
454
|
-
|
|
455
|
-
|
|
506
|
+
if /\A[0-9]+(?:\.[0-9]+)?\z/.match?(retry_after.strip)
|
|
507
|
+
seconds = Float(retry_after, exception: false)
|
|
508
|
+
return seconds && seconds.finite? && seconds <= RETRY_AFTER_CAP ? seconds : nil
|
|
509
|
+
end
|
|
456
510
|
begin
|
|
457
|
-
|
|
511
|
+
seconds = [Time.httpdate(retry_after) - Time.now, 0].max
|
|
512
|
+
return seconds > RETRY_AFTER_CAP ? nil : seconds
|
|
458
513
|
rescue ArgumentError
|
|
459
514
|
# Fall back to jitter when the header is not a delay or HTTP date.
|
|
460
515
|
end
|
|
461
516
|
end
|
|
462
|
-
rand * 0.25 * (2**attempt)
|
|
517
|
+
rand * [0.25 * (2**[attempt, 5].min), RETRY_AFTER_CAP].min
|
|
463
518
|
end
|
|
464
519
|
|
|
465
520
|
def retries_for(path, params)
|
|
@@ -470,7 +525,7 @@ module ParseAPI
|
|
|
470
525
|
metered ? 0 : DEFAULT_RETRIES
|
|
471
526
|
end
|
|
472
527
|
|
|
473
|
-
def build_error(status, body)
|
|
528
|
+
def build_error(status, body, retry_after = nil)
|
|
474
529
|
parsed = begin
|
|
475
530
|
JSON.parse(body)
|
|
476
531
|
rescue JSON::ParserError
|
|
@@ -482,7 +537,8 @@ module ParseAPI
|
|
|
482
537
|
code: parsed['code'].is_a?(String) ? parsed['code'] : 'unknown_error',
|
|
483
538
|
message: parsed['message'].is_a?(String) ? parsed['message'] : "Request failed with status #{status}",
|
|
484
539
|
docs: parsed['docs'].is_a?(String) ? parsed['docs'] : nil,
|
|
485
|
-
request_id: parsed['request_id'].is_a?(String) ? parsed['request_id'] : nil
|
|
540
|
+
request_id: parsed['request_id'].is_a?(String) ? parsed['request_id'] : nil,
|
|
541
|
+
retry_after: retry_after
|
|
486
542
|
)
|
|
487
543
|
end
|
|
488
544
|
end
|
data/lib/parseapi/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: parseapi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ParseAPI
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description:
|
|
14
14
|
email:
|