algolia 3.43.0 → 3.45.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: 26e15a7a80471a7a68cf607c70e21c52f1a0f5d12b3a93888d3865f5bc02a257
4
- data.tar.gz: 0c45427919aef09a76d67b2320ad80908268fb5e9d96a6e9f1510c25b8fab6bf
3
+ metadata.gz: 131af010718e4264c9274e60d2196d44d4b2fc773c50bf2efee519882241834b
4
+ data.tar.gz: c03e511630bd7fc5eced4798c576ef1b6fb2e77325929e5f38f4e788428e83bb
5
5
  SHA512:
6
- metadata.gz: 8f2f198904f5afb8bc656d3d6f4eadfb37330db685656144c978443b0614aa5faf64718124200fcde6d19234b18e48aa960b173ab9e5222cb04ccc7d1dd2fed7
7
- data.tar.gz: a4971f5c0ffc4fbddba53281df8763082e659a39f67cb53ffc1b5f76898b8269f4e544575727742d8e048ae4dea79d1b2cfc60639550a38d74203d94572099b9
6
+ metadata.gz: 21d7d96dddef5d1477718c2a29be54b607f2e182779eb7837bc279ce4cf05a3e7209584d99f61f8cf74549c928bbc07855987e70e36143d281fb226fef137dac
7
+ data.tar.gz: 444596073256d8222b5fde7e3b5172048408c487cb758243775b2c95d5fd0ec6401cff9b6fc2ba815bbcdc6db74a8cd9d9fc8b169eecba0c6d4988aa7e6270b2
@@ -3,9 +3,8 @@ on:
3
3
  issues:
4
4
  types: [opened]
5
5
 
6
- permissions:
7
- issues: write
8
- contents: read
6
+ # the sync script only reads the event payload and calls the Jira API with its own token
7
+ permissions: {}
9
8
 
10
9
  jobs:
11
10
  sync:
@@ -13,6 +12,9 @@ jobs:
13
12
  steps:
14
13
  - name: Create ticket
15
14
  uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
15
+ env:
16
+ # env indirection so the secret is never materialized into the composed script
17
+ JIRA_TOKEN: ${{ secrets.JIRA_TOKEN }}
16
18
  with:
17
19
  script: |
18
20
  const action = context.payload.action;
@@ -27,7 +29,7 @@ jobs:
27
29
  headers: {
28
30
  'Accept': 'application/json',
29
31
  'Content-Type': 'application/json',
30
- 'Authorization': `Basic ${{ secrets.JIRA_TOKEN }}`
32
+ 'Authorization': `Basic ${process.env.JIRA_TOKEN}`
31
33
  },
32
34
  body: JSON.stringify({
33
35
  fields: {
@@ -24,6 +24,6 @@ jobs:
24
24
  ruby-version: 4.0.6
25
25
  bundler-cache: true
26
26
 
27
- - uses: rubygems/release-gem@052cc82692552de3ef2b81fd670e41d13cba8092 # v1.4.0
27
+ - uses: rubygems/release-gem@7f9650160c1a4e7989fdc9855807bdbd421d8b6b # v1.4.1
28
28
  with:
29
29
  await-release: false
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## [3.45.0](https://github.com/algolia/algoliasearch-client-ruby/compare/3.44.0...3.45.0)
2
+
3
+ BREAKING CHANGES: this minor version includes breaking changes in the A/B Testing V3 API. See below for more details.
4
+
5
+ - [c974374aff](https://github.com/algolia/api-clients-automation/commit/c974374aff) feat(ruby): Request-ID and Correlation-ID support ([#6877](https://github.com/algolia/api-clients-automation/pull/6877)) by [@MarioAlexandruDan](https://github.com/MarioAlexandruDan/)
6
+ - [e4a6ab510a](https://github.com/algolia/api-clients-automation/commit/e4a6ab510a) chore(ci): fail CI if a personal access token secret is referenced ([#6919](https://github.com/algolia/api-clients-automation/pull/6919)) by [@eric-zaharia](https://github.com/eric-zaharia/)
7
+ - [085364de00](https://github.com/algolia/api-clients-automation/commit/085364de00) feat(specs): deprecates /2 abtests endpoints ([#6921](https://github.com/algolia/api-clients-automation/pull/6921)) by [@Niamsy](https://github.com/Niamsy/)
8
+ - [32c1aaa192](https://github.com/algolia/api-clients-automation/commit/32c1aaa192) chore(deps): dependencies 2026-08-24 ([#6922](https://github.com/algolia/api-clients-automation/pull/6922)) by [@algolia-api-clients-automation-bot[bot]](https://github.com/apps/algolia-api-clients-automation-bot)
9
+ - [28aa13de2e](https://github.com/algolia/api-clients-automation/commit/28aa13de2e) fix(clients): support guzzlehttp/psr7 v3 ([#6945](https://github.com/algolia/api-clients-automation/pull/6945)) by [@eric-zaharia](https://github.com/eric-zaharia/)
10
+ - [b4c4109163](https://github.com/algolia/api-clients-automation/commit/b4c4109163) fix(specs): flag analytics patterns endpoints with x-beta ([#6954](https://github.com/algolia/api-clients-automation/pull/6954)) by [@cdhawke](https://github.com/cdhawke/)
11
+ - [8317b3a8ef](https://github.com/algolia/api-clients-automation/commit/8317b3a8ef) feat(specs): BREAKING CHANGE – remove migratedAbTestID from abtesting-v3 ([#6883](https://github.com/algolia/api-clients-automation/pull/6883)) by [@cdhawke](https://github.com/cdhawke/)
12
+ - [808f5ebcea](https://github.com/algolia/api-clients-automation/commit/808f5ebcea) fix(ruby): keep millisecond precision when converting timeouts to seconds ([#6946](https://github.com/algolia/api-clients-automation/pull/6946)) by [@MarioAlexandruDan](https://github.com/MarioAlexandruDan/)
13
+
14
+ ## [3.44.0](https://github.com/algolia/algoliasearch-client-ruby/compare/3.43.0...3.44.0)
15
+
16
+ - [c974374aff](https://github.com/algolia/api-clients-automation/commit/c974374aff) feat(ruby): Request-ID and Correlation-ID support ([#6877](https://github.com/algolia/api-clients-automation/pull/6877)) by [@MarioAlexandruDan](https://github.com/MarioAlexandruDan/)
17
+ - [e4a6ab510a](https://github.com/algolia/api-clients-automation/commit/e4a6ab510a) chore(ci): fail CI if a personal access token secret is referenced ([#6919](https://github.com/algolia/api-clients-automation/pull/6919)) by [@eric-zaharia](https://github.com/eric-zaharia/)
18
+ - [085364de00](https://github.com/algolia/api-clients-automation/commit/085364de00) feat(specs): deprecates /2 abtests endpoints ([#6921](https://github.com/algolia/api-clients-automation/pull/6921)) by [@Niamsy](https://github.com/Niamsy/)
19
+ - [32c1aaa192](https://github.com/algolia/api-clients-automation/commit/32c1aaa192) chore(deps): dependencies 2026-08-24 ([#6922](https://github.com/algolia/api-clients-automation/pull/6922)) by [@algolia-api-clients-automation-bot[bot]](https://github.com/apps/algolia-api-clients-automation-bot)
20
+
1
21
  ## [3.43.0](https://github.com/algolia/algoliasearch-client-ruby/compare/3.42.3...3.43.0)
2
22
 
3
23
  - [3bee6d1dee](https://github.com/algolia/api-clients-automation/commit/3bee6d1dee) chore(ci): pin all GitHub Action refs to commit SHAs (API-475) ([#6819](https://github.com/algolia/api-clients-automation/pull/6819)) by [@MarioAlexandruDan](https://github.com/MarioAlexandruDan/)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- algolia (3.43.0)
4
+ algolia (3.45.0)
5
5
  base64 (>= 0.2.0, < 1)
6
6
  faraday (>= 1.0.1, < 3.0)
7
7
  faraday-net_http_persistent (>= 0.15, < 3)
@@ -23,7 +23,8 @@ module Algolia
23
23
  config.write_timeout = 30000
24
24
  end
25
25
 
26
- @api_client = Algolia::ApiClient.new(config)
26
+ # Per-client capability; an explicit config.request_id_enabled always wins.
27
+ @api_client = Algolia::ApiClient.new(config, request_id_support: false)
27
28
  end
28
29
 
29
30
  def self.create(app_id, api_key, region = nil, opts = {})
@@ -69,10 +70,11 @@ module Algolia
69
70
  self
70
71
  end
71
72
 
72
- # Creates a new A/B test.
73
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `POST /3/abtests`. Creates a new A/B test.
73
74
  #
74
75
  # Required API Key ACLs:
75
76
  # - editSettings
77
+ # THIS OPERATION IS DEPRECATED
76
78
  # @param add_ab_tests_request [AddABTestsRequest] (required)
77
79
  # @param request_options: The request options to send along with the query, they will be merged with the transporter base parameters (headers, query params, timeouts, etc.). (optional)
78
80
  # @return [Http::Response] the response
@@ -101,7 +103,7 @@ module Algolia
101
103
  @api_client.call_api(:POST, path, new_options)
102
104
  end
103
105
 
104
- # Creates a new A/B test.
106
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `POST /3/abtests`. Creates a new A/B test.
105
107
  #
106
108
  # Required API Key ACLs:
107
109
  # - editSettings
@@ -305,10 +307,11 @@ module Algolia
305
307
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Object")
306
308
  end
307
309
 
308
- # Deletes an A/B test by its ID.
310
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `DELETE /3/abtests/{id}`. Deletes an A/B test by its ID.
309
311
  #
310
312
  # Required API Key ACLs:
311
313
  # - editSettings
314
+ # THIS OPERATION IS DEPRECATED
312
315
  # @param id [Integer] Unique A/B test identifier. (required)
313
316
  # @param request_options: The request options to send along with the query, they will be merged with the transporter base parameters (headers, query params, timeouts, etc.). (optional)
314
317
  # @return [Http::Response] the response
@@ -337,7 +340,7 @@ module Algolia
337
340
  @api_client.call_api(:DELETE, path, new_options)
338
341
  end
339
342
 
340
- # Deletes an A/B test by its ID.
343
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `DELETE /3/abtests/{id}`. Deletes an A/B test by its ID.
341
344
  #
342
345
  # Required API Key ACLs:
343
346
  # - editSettings
@@ -349,10 +352,11 @@ module Algolia
349
352
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Abtesting::ABTestResponse")
350
353
  end
351
354
 
352
- # Given the traffic percentage and the expected effect size, this endpoint estimates the sample size and duration of an A/B test based on historical traffic.
355
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `POST /3/abtests/estimate`. Given the traffic percentage and the expected effect size, this endpoint estimates the sample size and duration of an A/B test based on historical traffic.
353
356
  #
354
357
  # Required API Key ACLs:
355
358
  # - analytics
359
+ # THIS OPERATION IS DEPRECATED
356
360
  # @param estimate_ab_test_request [EstimateABTestRequest] (required)
357
361
  # @param request_options: The request options to send along with the query, they will be merged with the transporter base parameters (headers, query params, timeouts, etc.). (optional)
358
362
  # @return [Http::Response] the response
@@ -381,7 +385,7 @@ module Algolia
381
385
  @api_client.call_api(:POST, path, new_options)
382
386
  end
383
387
 
384
- # Given the traffic percentage and the expected effect size, this endpoint estimates the sample size and duration of an A/B test based on historical traffic.
388
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `POST /3/abtests/estimate`. Given the traffic percentage and the expected effect size, this endpoint estimates the sample size and duration of an A/B test based on historical traffic.
385
389
  #
386
390
  # Required API Key ACLs:
387
391
  # - analytics
@@ -393,10 +397,11 @@ module Algolia
393
397
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Abtesting::EstimateABTestResponse")
394
398
  end
395
399
 
396
- # Retrieves the details for an A/B test by its ID.
400
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `GET /3/abtests/{id}`. Retrieves the details for an A/B test by its ID.
397
401
  #
398
402
  # Required API Key ACLs:
399
403
  # - analytics
404
+ # THIS OPERATION IS DEPRECATED
400
405
  # @param id [Integer] Unique A/B test identifier. (required)
401
406
  # @param request_options: The request options to send along with the query, they will be merged with the transporter base parameters (headers, query params, timeouts, etc.). (optional)
402
407
  # @return [Http::Response] the response
@@ -425,7 +430,7 @@ module Algolia
425
430
  @api_client.call_api(:GET, path, new_options)
426
431
  end
427
432
 
428
- # Retrieves the details for an A/B test by its ID.
433
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `GET /3/abtests/{id}`. Retrieves the details for an A/B test by its ID.
429
434
  #
430
435
  # Required API Key ACLs:
431
436
  # - analytics
@@ -437,10 +442,11 @@ module Algolia
437
442
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Abtesting::ABTest")
438
443
  end
439
444
 
440
- # Lists all A/B tests you configured for this application.
445
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `GET /3/abtests`. Lists all A/B tests you configured for this application.
441
446
  #
442
447
  # Required API Key ACLs:
443
448
  # - analytics
449
+ # THIS OPERATION IS DEPRECATED
444
450
  # @param offset [Integer] Position of the first item to return. (default to 0)
445
451
  # @param limit [Integer] Number of items to return. (default to 10)
446
452
  # @param index_prefix [String] Index name prefix. Only A/B tests for indices starting with this string are included in the response.
@@ -477,7 +483,7 @@ module Algolia
477
483
  @api_client.call_api(:GET, path, new_options)
478
484
  end
479
485
 
480
- # Lists all A/B tests you configured for this application.
486
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `GET /3/abtests`. Lists all A/B tests you configured for this application.
481
487
  #
482
488
  # Required API Key ACLs:
483
489
  # - analytics
@@ -492,10 +498,11 @@ module Algolia
492
498
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Abtesting::ListABTestsResponse")
493
499
  end
494
500
 
495
- # Stops an A/B test by its ID. You can't restart stopped A/B tests.
501
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `POST /3/abtests/{id}/stop`. Stops an A/B test by its ID. You can't restart stopped A/B tests.
496
502
  #
497
503
  # Required API Key ACLs:
498
504
  # - editSettings
505
+ # THIS OPERATION IS DEPRECATED
499
506
  # @param id [Integer] Unique A/B test identifier. (required)
500
507
  # @param request_options: The request options to send along with the query, they will be merged with the transporter base parameters (headers, query params, timeouts, etc.). (optional)
501
508
  # @return [Http::Response] the response
@@ -524,7 +531,7 @@ module Algolia
524
531
  @api_client.call_api(:POST, path, new_options)
525
532
  end
526
533
 
527
- # Stops an A/B test by its ID. You can't restart stopped A/B tests.
534
+ # This endpoint is deprecated. Use the A/B Testing API v3 instead. Replaced by `POST /3/abtests/{id}/stop`. Stops an A/B test by its ID. You can't restart stopped A/B tests.
528
535
  #
529
536
  # Required API Key ACLs:
530
537
  # - editSettings
@@ -23,7 +23,8 @@ module Algolia
23
23
  config.write_timeout = 30000
24
24
  end
25
25
 
26
- @api_client = Algolia::ApiClient.new(config)
26
+ # Per-client capability; an explicit config.request_id_enabled always wins.
27
+ @api_client = Algolia::ApiClient.new(config, request_id_support: false)
27
28
  end
28
29
 
29
30
  def self.create(app_id, api_key, region = nil, opts = {})
@@ -26,7 +26,8 @@ module Algolia
26
26
  config.write_timeout = 25000
27
27
  end
28
28
 
29
- @api_client = Algolia::ApiClient.new(config)
29
+ # Per-client capability; an explicit config.request_id_enabled always wins.
30
+ @api_client = Algolia::ApiClient.new(config, request_id_support: false)
30
31
  end
31
32
 
32
33
  def self.create(app_id, api_key, opts = {})
@@ -23,7 +23,8 @@ module Algolia
23
23
  config.write_timeout = 30000
24
24
  end
25
25
 
26
- @api_client = Algolia::ApiClient.new(config)
26
+ # Per-client capability; an explicit config.request_id_enabled always wins.
27
+ @api_client = Algolia::ApiClient.new(config, request_id_support: false)
27
28
  end
28
29
 
29
30
  def self.create(app_id, api_key, region = nil, opts = {})
@@ -691,7 +692,7 @@ module Algolia
691
692
  )
692
693
  end
693
694
 
694
- # **Beta**: this endpoint is under active development and may change without notice. Returns the static catalog of analytics fields, grouped by domain and usage (metrics, filters, groups, distributions). No authentication is required. Use it to discover valid `(domain, kind)` pairs before building the other `/3/patterns/*` queries; two fields are combinable in one query only when their `roots` intersect. Each entry's `requires` lists the ACLs needed when that field is actually used in a query.
695
+ # Returns the static catalog of analytics fields, grouped by domain and usage (metrics, filters, groups, distributions). No authentication is required. Use it to discover valid `(domain, kind)` pairs before building the other `/3/patterns/*` queries; two fields are combinable in one query only when their `roots` intersect. Each entry's `requires` lists the ACLs needed when that field is actually used in a query.
695
696
 
696
697
  # @param request_options: The request options to send along with the query, they will be merged with the transporter base parameters (headers, query params, timeouts, etc.). (optional)
697
698
  # @return [Http::Response] the response
@@ -715,7 +716,7 @@ module Algolia
715
716
  @api_client.call_api(:GET, path, new_options)
716
717
  end
717
718
 
718
- # **Beta**: this endpoint is under active development and may change without notice. Returns the static catalog of analytics fields, grouped by domain and usage (metrics, filters, groups, distributions). No authentication is required. Use it to discover valid `(domain, kind)` pairs before building the other `/3/patterns/*` queries; two fields are combinable in one query only when their `roots` intersect. Each entry's `requires` lists the ACLs needed when that field is actually used in a query.
719
+ # Returns the static catalog of analytics fields, grouped by domain and usage (metrics, filters, groups, distributions). No authentication is required. Use it to discover valid `(domain, kind)` pairs before building the other `/3/patterns/*` queries; two fields are combinable in one query only when their `roots` intersect. Each entry's `requires` lists the ACLs needed when that field is actually used in a query.
719
720
 
720
721
  # @param request_options: The request options to send along with the query, they will be merged with the transporter base parameters (headers, query params, timeouts, etc.). (optional)
721
722
  # @return [Catalog]
@@ -1798,7 +1799,7 @@ module Algolia
1798
1799
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Analytics::GetUsersCountResponse")
1799
1800
  end
1800
1801
 
1801
- # **Beta**: this endpoint is under active development and may change without notice. Buckets one or more numeric fields into histograms and returns an object keyed by `histogram<Field>`, each mapping a bin label to a count. `distributions` and `parameters` are required; `filters` is optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1802
+ # Buckets one or more numeric fields into histograms and returns an object keyed by `histogram<Field>`, each mapping a bin label to a count. `distributions` and `parameters` are required; `filters` is optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1802
1803
  #
1803
1804
  # Required API Key ACLs:
1804
1805
  # - analytics
@@ -1832,7 +1833,7 @@ module Algolia
1832
1833
  @api_client.call_api(:POST, path, new_options)
1833
1834
  end
1834
1835
 
1835
- # **Beta**: this endpoint is under active development and may change without notice. Buckets one or more numeric fields into histograms and returns an object keyed by `histogram<Field>`, each mapping a bin label to a count. `distributions` and `parameters` are required; `filters` is optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1836
+ # Buckets one or more numeric fields into histograms and returns an object keyed by `histogram<Field>`, each mapping a bin label to a count. `distributions` and `parameters` are required; `filters` is optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1836
1837
  #
1837
1838
  # Required API Key ACLs:
1838
1839
  # - analytics
@@ -1845,7 +1846,7 @@ module Algolia
1845
1846
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Hash<String, Object>")
1846
1847
  end
1847
1848
 
1848
- # **Beta**: this endpoint is under active development and may change without notice. Aggregates the requested `metrics` over the whole period and returns a single object keyed by metric kind. `metrics` and `parameters` are required; `filters` is optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1849
+ # Aggregates the requested `metrics` over the whole period and returns a single object keyed by metric kind. `metrics` and `parameters` are required; `filters` is optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1849
1850
  #
1850
1851
  # Required API Key ACLs:
1851
1852
  # - analytics
@@ -1879,7 +1880,7 @@ module Algolia
1879
1880
  @api_client.call_api(:POST, path, new_options)
1880
1881
  end
1881
1882
 
1882
- # **Beta**: this endpoint is under active development and may change without notice. Aggregates the requested `metrics` over the whole period and returns a single object keyed by metric kind. `metrics` and `parameters` are required; `filters` is optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1883
+ # Aggregates the requested `metrics` over the whole period and returns a single object keyed by metric kind. `metrics` and `parameters` are required; `filters` is optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1883
1884
  #
1884
1885
  # Required API Key ACLs:
1885
1886
  # - analytics
@@ -1892,7 +1893,7 @@ module Algolia
1892
1893
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Hash<String, Object>")
1893
1894
  end
1894
1895
 
1895
- # **Beta**: this endpoint is under active development and may change without notice. Returns `rows`, each a flat object of the requested fields. `metrics` and `parameters` are required; `groupBy`, `filters`, and `orderBy` are optional, though `orderBy` is required when `groupBy` is set. Discover valid field kinds per domain with `/3/patterns/fields`.
1896
+ # Returns `rows`, each a flat object of the requested fields. `metrics` and `parameters` are required; `groupBy`, `filters`, and `orderBy` are optional, though `orderBy` is required when `groupBy` is set. Discover valid field kinds per domain with `/3/patterns/fields`.
1896
1897
  #
1897
1898
  # Required API Key ACLs:
1898
1899
  # - analytics
@@ -1926,7 +1927,7 @@ module Algolia
1926
1927
  @api_client.call_api(:POST, path, new_options)
1927
1928
  end
1928
1929
 
1929
- # **Beta**: this endpoint is under active development and may change without notice. Returns `rows`, each a flat object of the requested fields. `metrics` and `parameters` are required; `groupBy`, `filters`, and `orderBy` are optional, though `orderBy` is required when `groupBy` is set. Discover valid field kinds per domain with `/3/patterns/fields`.
1930
+ # Returns `rows`, each a flat object of the requested fields. `metrics` and `parameters` are required; `groupBy`, `filters`, and `orderBy` are optional, though `orderBy` is required when `groupBy` is set. Discover valid field kinds per domain with `/3/patterns/fields`.
1930
1931
  #
1931
1932
  # Required API Key ACLs:
1932
1933
  # - analytics
@@ -1939,7 +1940,7 @@ module Algolia
1939
1940
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Analytics::TableResponse")
1940
1941
  end
1941
1942
 
1942
- # **Beta**: this endpoint is under active development and may change without notice. Returns one time series per `groupBy` combination, each with period `totals` and a per-day metric breakdown. `metrics` and `parameters` are required; `groupBy` and `filters` are optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1943
+ # Returns one time series per `groupBy` combination, each with period `totals` and a per-day metric breakdown. `metrics` and `parameters` are required; `groupBy` and `filters` are optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1943
1944
  #
1944
1945
  # Required API Key ACLs:
1945
1946
  # - analytics
@@ -1973,7 +1974,7 @@ module Algolia
1973
1974
  @api_client.call_api(:POST, path, new_options)
1974
1975
  end
1975
1976
 
1976
- # **Beta**: this endpoint is under active development and may change without notice. Returns one time series per `groupBy` combination, each with period `totals` and a per-day metric breakdown. `metrics` and `parameters` are required; `groupBy` and `filters` are optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1977
+ # Returns one time series per `groupBy` combination, each with period `totals` and a per-day metric breakdown. `metrics` and `parameters` are required; `groupBy` and `filters` are optional. Discover valid field kinds per domain with `/3/patterns/fields`.
1977
1978
  #
1978
1979
  # Required API Key ACLs:
1979
1980
  # - analytics
@@ -23,7 +23,8 @@ module Algolia
23
23
  config.write_timeout = 30000
24
24
  end
25
25
 
26
- @api_client = Algolia::ApiClient.new(config)
26
+ # Per-client capability; an explicit config.request_id_enabled always wins.
27
+ @api_client = Algolia::ApiClient.new(config, request_id_support: true)
27
28
  end
28
29
 
29
30
  def self.create(app_id, api_key, opts = {})
@@ -23,7 +23,8 @@ module Algolia
23
23
  config.write_timeout = 25000
24
24
  end
25
25
 
26
- @api_client = Algolia::ApiClient.new(config)
26
+ # Per-client capability; an explicit config.request_id_enabled always wins.
27
+ @api_client = Algolia::ApiClient.new(config, request_id_support: false)
27
28
  end
28
29
 
29
30
  def self.create(app_id, api_key, region = nil, opts = {})
@@ -23,7 +23,8 @@ module Algolia
23
23
  config.write_timeout = 30000
24
24
  end
25
25
 
26
- @api_client = Algolia::ApiClient.new(config)
26
+ # Per-client capability; an explicit config.request_id_enabled always wins.
27
+ @api_client = Algolia::ApiClient.new(config, request_id_support: false)
27
28
  end
28
29
 
29
30
  def self.create(app_id, api_key, region = nil, opts = {})
@@ -23,7 +23,8 @@ module Algolia
23
23
  config.write_timeout = 30000
24
24
  end
25
25
 
26
- @api_client = Algolia::ApiClient.new(config)
26
+ # Per-client capability; an explicit config.request_id_enabled always wins.
27
+ @api_client = Algolia::ApiClient.new(config, request_id_support: false)
27
28
  end
28
29
 
29
30
  def self.create(app_id, api_key, opts = {})
@@ -23,7 +23,8 @@ module Algolia
23
23
  config.write_timeout = 30000
24
24
  end
25
25
 
26
- @api_client = Algolia::ApiClient.new(config)
26
+ # Per-client capability; an explicit config.request_id_enabled always wins.
27
+ @api_client = Algolia::ApiClient.new(config, request_id_support: false)
27
28
  end
28
29
 
29
30
  def self.create(app_id, api_key, region = nil, opts = {})
@@ -23,7 +23,8 @@ module Algolia
23
23
  config.write_timeout = 30000
24
24
  end
25
25
 
26
- @api_client = Algolia::ApiClient.new(config)
26
+ # Per-client capability; an explicit config.request_id_enabled always wins.
27
+ @api_client = Algolia::ApiClient.new(config, request_id_support: false)
27
28
  end
28
29
 
29
30
  def self.create(app_id, api_key, region = nil, opts = {})
@@ -23,7 +23,8 @@ module Algolia
23
23
  config.write_timeout = 30000
24
24
  end
25
25
 
26
- @api_client = Algolia::ApiClient.new(config)
26
+ # Per-client capability; an explicit config.request_id_enabled always wins.
27
+ @api_client = Algolia::ApiClient.new(config, request_id_support: true)
27
28
  end
28
29
 
29
30
  def self.create(app_id, api_key, opts = {})
@@ -69,7 +69,8 @@ module Algolia
69
69
  config.write_timeout = 30000
70
70
  end
71
71
 
72
- @api_client = Algolia::ApiClient.new(config)
72
+ # Per-client capability; an explicit config.request_id_enabled always wins.
73
+ @api_client = Algolia::ApiClient.new(config, request_id_support: true)
73
74
  @ingestion_transporter = nil
74
75
  if config.transformation_options
75
76
  @ingestion_transporter = _build_ingestion_transporter(config.transformation_options)
@@ -3412,6 +3413,42 @@ module Algolia
3412
3413
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Search::UpdateApiKeyResponse")
3413
3414
  end
3414
3415
 
3416
+ # Helper: Derives the request options carrying the Request-ID shared by every request
3417
+ # of one helper invocation. Returns the options untouched when the client does not
3418
+ # support Request-ID or the caller already supplied one through the options (as a
3419
+ # header or an x-algolia-request-id query parameter) or the config headers, which
3420
+ # also makes nested helpers reuse the ID minted by their caller.
3421
+ #
3422
+ # @param request_options [Hash]
3423
+ #
3424
+ # @return [Hash]
3425
+ private def with_request_id(request_options = {})
3426
+ config = api_client.config
3427
+ unless api_client.request_id_enabled? &&
3428
+ !Transport::RequestId.request_id?(request_options[:header_params]) &&
3429
+ !Transport::RequestId.request_id?(config.header_params) &&
3430
+ !Transport::RequestId.request_id_query_param?(request_options[:query_params])
3431
+ return request_options
3432
+ end
3433
+
3434
+ request_options.merge(
3435
+ :header_params => (request_options[:header_params] || {}).merge(
3436
+ Transport::RequestId::HEADER => Transport::RequestId.generate
3437
+ )
3438
+ )
3439
+ end
3440
+
3441
+ # Helper: Drops the caller's timeouts from the given request options, so that the
3442
+ # failure-path cleanup calls run with the client defaults while keeping the other
3443
+ # options, notably the headers carrying the invocation's shared Request-ID.
3444
+ #
3445
+ # @param request_options [Hash]
3446
+ #
3447
+ # @return [Hash]
3448
+ private def without_timeouts(request_options)
3449
+ request_options.reject { |key, _| key == :timeout || key == :connect_timeout }
3450
+ end
3451
+
3415
3452
  # The parent search config MUST NOT leak into the ingestion transporter.
3416
3453
  def _build_ingestion_transporter(transformation_options)
3417
3454
  hosts = if transformation_options.hosts
@@ -3561,6 +3598,10 @@ module Algolia
3561
3598
  )
3562
3599
  assert_ingestion_transporter!
3563
3600
 
3601
+ # The shared Request-ID only covers the search-side calls: the ingestion
3602
+ # push goes to an API that must not receive the header.
3603
+ search_request_options = with_request_id(request_options)
3604
+
3564
3605
  if objects.empty?
3565
3606
  @api_client.logger.warn(
3566
3607
  "replace_all_objects_with_transformation was called with an empty list of objects, which will delete all records currently in the \"#{index_name}\" index."
@@ -3581,7 +3622,7 @@ module Algolia
3581
3622
  destination: tmp_index_name,
3582
3623
  scope: scopes
3583
3624
  ),
3584
- request_options
3625
+ search_request_options
3585
3626
  )
3586
3627
 
3587
3628
  watch_responses = @ingestion_transporter.chunked_push(
@@ -3595,7 +3636,13 @@ module Algolia
3595
3636
  opts
3596
3637
  )
3597
3638
 
3598
- wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
3639
+ wait_for_task(
3640
+ tmp_index_name,
3641
+ copy_operation_response.task_id,
3642
+ opts.max_retries,
3643
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3644
+ search_request_options
3645
+ )
3599
3646
 
3600
3647
  copy_operation_response = operation_index(
3601
3648
  index_name,
@@ -3604,10 +3651,16 @@ module Algolia
3604
3651
  destination: tmp_index_name,
3605
3652
  scope: scopes
3606
3653
  ),
3607
- request_options
3654
+ search_request_options
3608
3655
  )
3609
3656
 
3610
- wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
3657
+ wait_for_task(
3658
+ tmp_index_name,
3659
+ copy_operation_response.task_id,
3660
+ opts.max_retries,
3661
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3662
+ search_request_options
3663
+ )
3611
3664
 
3612
3665
  move_operation_response = operation_index(
3613
3666
  tmp_index_name,
@@ -3615,10 +3668,16 @@ module Algolia
3615
3668
  operation: Search::OperationType::MOVE,
3616
3669
  destination: index_name
3617
3670
  ),
3618
- request_options
3671
+ search_request_options
3619
3672
  )
3620
3673
 
3621
- wait_for_task(tmp_index_name, move_operation_response.task_id, opts.max_retries)
3674
+ wait_for_task(
3675
+ tmp_index_name,
3676
+ move_operation_response.task_id,
3677
+ opts.max_retries,
3678
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3679
+ search_request_options
3680
+ )
3622
3681
 
3623
3682
  search_watch_responses = watch_responses.map do |wr|
3624
3683
  Search::WatchResponse.build_from_hash(wr.to_hash)
@@ -3630,7 +3689,13 @@ module Algolia
3630
3689
  move_operation_response: move_operation_response
3631
3690
  )
3632
3691
  rescue Exception => e
3633
- delete_index(tmp_index_name)
3692
+ begin
3693
+ delete_index(tmp_index_name, without_timeouts(search_request_options))
3694
+ rescue StandardError => cleanup_error
3695
+ @api_client.logger.warn(
3696
+ "Failed to delete the temporary index \"#{tmp_index_name}\", please delete it manually: #{cleanup_error}"
3697
+ )
3698
+ end
3634
3699
 
3635
3700
  raise e
3636
3701
  end
@@ -3641,16 +3706,17 @@ module Algolia
3641
3706
  # @param index_name [String] the `index_name` where the operation was performed. (required)
3642
3707
  # @param task_id [Integer] the `task_id` returned in the method response. (required)
3643
3708
  # @param max_retries [Integer] the maximum number of retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES)
3644
- # @param timeout [Proc] the function to decide how long to wait between retries. (optional)
3709
+ # @param timeout [Proc] the function to decide how long to wait between retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT)
3645
3710
  # @param request_options [Hash] the requestOptions to send along with the query, they will be forwarded to the `get_task` method.
3646
3711
  # @return [Http::Response] the last get_task response
3647
3712
  def wait_for_task(
3648
3713
  index_name,
3649
3714
  task_id,
3650
3715
  max_retries = Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES,
3651
- timeout = -> (retry_count) { [retry_count * 200, 5000].min },
3716
+ timeout = Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3652
3717
  request_options = {}
3653
3718
  )
3719
+ request_options = with_request_id(request_options)
3654
3720
  retries = 0
3655
3721
  while retries < max_retries
3656
3722
  res = get_task(index_name, task_id, request_options)
@@ -3672,15 +3738,16 @@ module Algolia
3672
3738
  #
3673
3739
  # @param task_id [Integer] the `task_id` returned in the method response. (required)
3674
3740
  # @param max_retries [Integer] the maximum number of retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES)
3675
- # @param timeout [Proc] the function to decide how long to wait between retries. (optional)
3741
+ # @param timeout [Proc] the function to decide how long to wait between retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT)
3676
3742
  # @param request_options [Hash] the requestOptions to send along with the query, they will be forwarded to the `get_task` method.
3677
3743
  # @return [Http::Response] the last get_task response
3678
3744
  def wait_for_app_task(
3679
3745
  task_id,
3680
3746
  max_retries = Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES,
3681
- timeout = -> (retry_count) { [retry_count * 200, 5000].min },
3747
+ timeout = Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3682
3748
  request_options = {}
3683
3749
  )
3750
+ request_options = with_request_id(request_options)
3684
3751
  retries = 0
3685
3752
  while retries < max_retries
3686
3753
  res = get_app_task(task_id, request_options)
@@ -3703,8 +3770,8 @@ module Algolia
3703
3770
  # @param key [String] the `key` that has been added, deleted or updated.
3704
3771
  # @param operation [String] the `operation` that was done on a `key`.
3705
3772
  # @param api_key [Hash] necessary to know if an `update` operation has been processed, compare fields of the response with it.
3706
- # @param max_retries [Integer] the maximum number of retries.
3707
- # @param timeout [Proc] the function to decide how long to wait between retries.
3773
+ # @param max_retries [Integer] the maximum number of retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES)
3774
+ # @param timeout [Proc] the function to decide how long to wait between retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT)
3708
3775
  # @param request_options [Hash] the requestOptions to send along with the query, they will be forwarded to the `getApikey` method and merged with the transporter requestOptions.
3709
3776
  # @return [Http::Response] the last get_api_key response
3710
3777
  def wait_for_api_key(
@@ -3712,9 +3779,10 @@ module Algolia
3712
3779
  operation,
3713
3780
  api_key = Search::ApiKey.new,
3714
3781
  max_retries = Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES,
3715
- timeout = -> (retry_count) { [retry_count * 200, 5000].min },
3782
+ timeout = Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3716
3783
  request_options = {}
3717
3784
  )
3785
+ request_options = with_request_id(request_options)
3718
3786
  api_key = api_client.object_to_hash(api_key)
3719
3787
 
3720
3788
  retries = 0
@@ -3769,6 +3837,7 @@ module Algolia
3769
3837
  # @param request_options [Hash] the requestOptions to send along with the query, they will be forwarded to the `browse` method.
3770
3838
  # @param block [Proc] the block to execute on each object of the index.
3771
3839
  def browse_objects(index_name, browse_params = Search::BrowseParamsObject.new, request_options = {}, &block)
3840
+ request_options = with_request_id(request_options)
3772
3841
  browse_params = api_client.object_to_hash(browse_params)
3773
3842
 
3774
3843
  browse_params[:hitsPerPage] = 1000 unless browse_params.key?(:hitsPerPage)
@@ -3798,6 +3867,7 @@ module Algolia
3798
3867
  # @param request_options [Hash] the requestOptions to send along with the query, they will be forwarded to the `searchRules` method.
3799
3868
  # @param block [Proc] the block to execute on each rule of the index.
3800
3869
  def browse_rules(index_name, search_rules_params = Search::SearchRulesParams.new, request_options = {}, &block)
3870
+ request_options = with_request_id(request_options)
3801
3871
  search_rules_params = api_client.object_to_hash(search_rules_params)
3802
3872
 
3803
3873
  search_rules_params[:page] ||= 0
@@ -3833,6 +3903,7 @@ module Algolia
3833
3903
  request_options = {},
3834
3904
  &block
3835
3905
  )
3906
+ request_options = with_request_id(request_options)
3836
3907
  search_synonyms_params = api_client.object_to_hash(search_synonyms_params)
3837
3908
 
3838
3909
  search_synonyms_params[:page] ||= 0
@@ -4036,6 +4107,7 @@ module Algolia
4036
4107
  request_options = {},
4037
4108
  chunked_options = nil
4038
4109
  )
4110
+ request_options = with_request_id(request_options)
4039
4111
  opts = Algolia::ChunkedHelperOptions.resolve(chunked_options)
4040
4112
  responses = []
4041
4113
  objects.each_slice(batch_size) do |chunk|
@@ -4048,7 +4120,13 @@ module Algolia
4048
4120
 
4049
4121
  if wait_for_tasks
4050
4122
  responses.each do |response|
4051
- wait_for_task(index_name, response.task_id, opts.max_retries)
4123
+ wait_for_task(
4124
+ index_name,
4125
+ response.task_id,
4126
+ opts.max_retries,
4127
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
4128
+ request_options
4129
+ )
4052
4130
  end
4053
4131
  end
4054
4132
 
@@ -4074,6 +4152,8 @@ module Algolia
4074
4152
  request_options = {},
4075
4153
  chunked_options = nil
4076
4154
  )
4155
+ request_options = with_request_id(request_options)
4156
+
4077
4157
  if objects.empty?
4078
4158
  @api_client.logger.warn(
4079
4159
  "replace_all_objects was called with an empty list of objects, which will delete all records currently in the \"#{index_name}\" index."
@@ -4107,7 +4187,13 @@ module Algolia
4107
4187
  opts
4108
4188
  )
4109
4189
 
4110
- wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
4190
+ wait_for_task(
4191
+ tmp_index_name,
4192
+ copy_operation_response.task_id,
4193
+ opts.max_retries,
4194
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
4195
+ request_options
4196
+ )
4111
4197
 
4112
4198
  copy_operation_response = operation_index(
4113
4199
  index_name,
@@ -4119,7 +4205,13 @@ module Algolia
4119
4205
  request_options
4120
4206
  )
4121
4207
 
4122
- wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
4208
+ wait_for_task(
4209
+ tmp_index_name,
4210
+ copy_operation_response.task_id,
4211
+ opts.max_retries,
4212
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
4213
+ request_options
4214
+ )
4123
4215
 
4124
4216
  move_operation_response = operation_index(
4125
4217
  tmp_index_name,
@@ -4130,7 +4222,13 @@ module Algolia
4130
4222
  request_options
4131
4223
  )
4132
4224
 
4133
- wait_for_task(tmp_index_name, move_operation_response.task_id, opts.max_retries)
4225
+ wait_for_task(
4226
+ tmp_index_name,
4227
+ move_operation_response.task_id,
4228
+ opts.max_retries,
4229
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
4230
+ request_options
4231
+ )
4134
4232
 
4135
4233
  Search::ReplaceAllObjectsResponse.new(
4136
4234
  copy_operation_response: copy_operation_response,
@@ -4138,7 +4236,13 @@ module Algolia
4138
4236
  move_operation_response: move_operation_response
4139
4237
  )
4140
4238
  rescue Exception => e
4141
- delete_index(tmp_index_name)
4239
+ begin
4240
+ delete_index(tmp_index_name, without_timeouts(request_options))
4241
+ rescue StandardError => cleanup_error
4242
+ @api_client.logger.warn(
4243
+ "Failed to delete the temporary index \"#{tmp_index_name}\", please delete it manually: #{cleanup_error}"
4244
+ )
4245
+ end
4142
4246
 
4143
4247
  raise e
4144
4248
  end
@@ -13,11 +13,19 @@ module Algolia
13
13
 
14
14
  # Initializes the ApiClient
15
15
  # @option config [Configuration] Configuration for initializing the object, default to Configuration.default
16
- def initialize(config = Configuration.default)
16
+ # @param request_id_support [true, false] the generated per-client Request-ID capability,
17
+ # applied when config.request_id_enabled is nil. Kept on the transport so a shared Configuration is never mutated.
18
+ def initialize(config = Configuration.default, request_id_support: false)
17
19
  @config = config
18
20
  @requester = config.requester || Http::HttpRequester.new("net_http_persistent", LoggerHelper.create)
19
21
  @logger = (@requester.logger if @requester.respond_to?(:logger)) || LoggerHelper.create
20
- @transporter = Transport::Transport.new(config, @requester)
22
+ @transporter = Transport::Transport.new(config, @requester, request_id_support: request_id_support)
23
+ end
24
+
25
+ # Whether the transport mints Request-ID headers, resolved per request.
26
+ # @return [true, false]
27
+ def request_id_enabled?
28
+ @transporter.request_id_enabled?
21
29
  end
22
30
 
23
31
  def self.default
@@ -3,6 +3,7 @@ module Algolia
3
3
  class ChunkedHelperOptions
4
4
  DEFAULT_MAX_RETRIES = 100
5
5
  DEFAULT_REPLACE_ALL_OBJECTS_MAX_RETRIES = 800
6
+ DEFAULT_TIMEOUT = -> (retry_count) { [retry_count * 200, 5000].min }
6
7
  attr_reader :max_retries
7
8
 
8
9
  def initialize(max_retries: DEFAULT_MAX_RETRIES)
@@ -1,5 +1,3 @@
1
- # Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT.
2
-
3
1
  module Algolia
4
2
  class Configuration
5
3
  attr_accessor(
@@ -21,6 +19,14 @@ module Algolia
21
19
  # @return [true, false]
22
20
  attr_accessor :client_side_validation
23
21
 
22
+ # Whether the transport sends a Request-ID header, minted once per call and
23
+ # reused across its retry attempts. When nil, each generated client applies its
24
+ # per-client default (on for the search, recommend and composition APIs, off
25
+ # elsewhere); an explicit true or false always wins and may be flipped at any
26
+ # time. A caller-supplied Request-ID is never overwritten.
27
+ # @return [true, false, nil]
28
+ attr_accessor :request_id_enabled
29
+
24
30
  def initialize(app_id, api_key, hosts, client_name, opts = {})
25
31
  @hosts = hosts
26
32
  @app_id = app_id
@@ -33,6 +39,8 @@ module Algolia
33
39
  @requester = opts[:requester]
34
40
  @transformation_options = opts[:transformation_options]
35
41
 
42
+ @request_id_enabled = opts[:request_id_enabled]
43
+
36
44
  @user_agent = UserAgent.new.add(client_name, VERSION)
37
45
 
38
46
  if opts[:user_agent_segments]
data/lib/algolia/error.rb CHANGED
@@ -12,13 +12,20 @@ module Algolia
12
12
  class AlgoliaUnreachableHostError < AlgoliaError
13
13
  attr_reader :errors
14
14
 
15
- def initialize(message, errors = [])
15
+ # The last non-empty Correlation-ID header among the retried attempts, or nil.
16
+ # Quote it when contacting Algolia support.
17
+ attr_reader :correlation_id
18
+
19
+ def initialize(message, errors = [], correlation_id = nil)
16
20
  errors.last&.tap do |last_error|
17
21
  message += " Last error for #{last_error[:host]}: #{last_error[:error]}"
18
22
  end
19
23
 
24
+ message += " (Correlation-ID: #{correlation_id})" unless correlation_id.nil? || correlation_id.empty?
25
+
20
26
  super(message)
21
27
  @errors = errors
28
+ @correlation_id = correlation_id
22
29
  end
23
30
  end
24
31
 
@@ -29,10 +36,20 @@ module Algolia
29
36
  class AlgoliaHttpError < AlgoliaError
30
37
  attr_accessor :code, :http_message
31
38
 
32
- def initialize(code, message)
39
+ # The Correlation-ID header of the failed response (possibly ""), or nil.
40
+ # Quote it when contacting Algolia support.
41
+ attr_reader :correlation_id
42
+
43
+ def initialize(code, message, correlation_id = nil)
33
44
  self.code = code
34
45
  self.http_message = message
35
- super("#{code}: #{message}")
46
+ @correlation_id = correlation_id
47
+
48
+ if correlation_id.nil? || correlation_id.empty?
49
+ super("#{code}: #{message}")
50
+ else
51
+ super("#{code}: #{message} (Correlation-ID: #{correlation_id})")
52
+ end
36
53
  end
37
54
  end
38
55
  end
@@ -33,9 +33,6 @@ module Algolia
33
33
 
34
34
  attr_accessor :configuration
35
35
 
36
- # Unique migrated A/B test identifier.
37
- attr_accessor :migrated_ab_test_id
38
-
39
36
  attr_accessor :decision
40
37
 
41
38
  # Attribute mapping from ruby-style variable name to JSON key.
@@ -50,7 +47,6 @@ module Algolia
50
47
  :status => :status,
51
48
  :variants => :variants,
52
49
  :configuration => :configuration,
53
- :migrated_ab_test_id => :migratedAbTestID,
54
50
  :decision => :decision
55
51
  }
56
52
  end
@@ -67,7 +63,6 @@ module Algolia
67
63
  :status => :"Status",
68
64
  :variants => :"Array<Variant>",
69
65
  :configuration => :"ABTestConfiguration",
70
- :migrated_ab_test_id => :"Integer",
71
66
  :decision => :"Decision"
72
67
  }
73
68
  end
@@ -153,10 +148,6 @@ module Algolia
153
148
  self.configuration = attributes[:configuration]
154
149
  end
155
150
 
156
- if attributes.key?(:migrated_ab_test_id)
157
- self.migrated_ab_test_id = attributes[:migrated_ab_test_id]
158
- end
159
-
160
151
  if attributes.key?(:decision)
161
152
  self.decision = attributes[:decision]
162
153
  end
@@ -176,7 +167,6 @@ module Algolia
176
167
  status == other.status &&
177
168
  variants == other.variants &&
178
169
  configuration == other.configuration &&
179
- migrated_ab_test_id == other.migrated_ab_test_id &&
180
170
  decision == other.decision
181
171
  end
182
172
 
@@ -189,19 +179,7 @@ module Algolia
189
179
  # Calculates hash code according to all attributes.
190
180
  # @return [Integer] Hash code
191
181
  def hash
192
- [
193
- ab_test_id,
194
- updated_at,
195
- created_at,
196
- end_at,
197
- stopped_at,
198
- name,
199
- status,
200
- variants,
201
- configuration,
202
- migrated_ab_test_id,
203
- decision
204
- ].hash
182
+ [ab_test_id, updated_at, created_at, end_at, stopped_at, name, status, variants, configuration, decision].hash
205
183
  end
206
184
 
207
185
  # Builds the object from hash
@@ -8,6 +8,9 @@ require "time"
8
8
  module Algolia
9
9
  module Search
10
10
  class Log
11
+ # Correlation ID of the logged API request, also returned in that request's `Correlation-ID` response header.
12
+ attr_accessor :cid
13
+
11
14
  # Date and time of the API request, in RFC 3339 format.
12
15
  attr_accessor :timestamp
13
16
 
@@ -56,6 +59,7 @@ module Algolia
56
59
  # Attribute mapping from ruby-style variable name to JSON key.
57
60
  def self.attribute_map
58
61
  {
62
+ :cid => :cid,
59
63
  :timestamp => :timestamp,
60
64
  :method => :method,
61
65
  :answer_code => :answer_code,
@@ -77,6 +81,7 @@ module Algolia
77
81
  # Attribute type mapping.
78
82
  def self.types_mapping
79
83
  {
84
+ :cid => :"String",
80
85
  :timestamp => :"String",
81
86
  :method => :"String",
82
87
  :answer_code => :"String",
@@ -122,6 +127,10 @@ module Algolia
122
127
  h[k.to_sym] = v
123
128
  }
124
129
 
130
+ if attributes.key?(:cid)
131
+ self.cid = attributes[:cid]
132
+ end
133
+
125
134
  if attributes.key?(:timestamp)
126
135
  self.timestamp = attributes[:timestamp]
127
136
  else
@@ -210,6 +219,7 @@ module Algolia
210
219
  def ==(other)
211
220
  return true if self.equal?(other)
212
221
  self.class == other.class &&
222
+ cid == other.cid &&
213
223
  timestamp == other.timestamp &&
214
224
  method == other.method &&
215
225
  answer_code == other.answer_code &&
@@ -237,6 +247,7 @@ module Algolia
237
247
  # @return [Integer] Hash code
238
248
  def hash
239
249
  [
250
+ cid,
240
251
  timestamp,
241
252
  method,
242
253
  answer_code,
@@ -26,8 +26,8 @@ module Algolia
26
26
  #
27
27
  def send_request(host, method, path, body, query_params, headers, timeout, connect_timeout)
28
28
  connection = connection(host)
29
- connection.options.timeout = timeout / 1000
30
- connection.options.open_timeout = connect_timeout / 1000
29
+ connection.options.timeout = timeout / 1000.0
30
+ connection.options.open_timeout = connect_timeout / 1000.0
31
31
  path += handle_query_params(query_params)
32
32
 
33
33
  @logger.info("Sending #{method.to_s.upcase} request to #{path} with body #{body}") if ENV["ALGOLIA_DEBUG"]
@@ -0,0 +1,49 @@
1
+ require "securerandom"
2
+
3
+ module Algolia
4
+ module Transport
5
+ # Mints the Request-ID tracing header sent by the clients that support it.
6
+ module RequestId
7
+ HEADER = "request-id".freeze
8
+ CORRELATION_HEADER = "Correlation-ID".freeze
9
+ QUERY_PARAM = "x-algolia-request-id".freeze
10
+
11
+ # Returns a fresh 11-character base62 identifier suitable for the Request-ID header.
12
+ #
13
+ # @return [String]
14
+ #
15
+ def self.generate
16
+ SecureRandom.alphanumeric(11)
17
+ end
18
+
19
+ # Whether the given headers already carry a Request-ID entry, whatever its casing
20
+ # and key type. Header hashes keep the caller's literal casing and may use symbol
21
+ # keys, so the lookup must not assume a canonical form.
22
+ #
23
+ # @param headers [Hash, nil]
24
+ #
25
+ # @return [true, false]
26
+ #
27
+ def self.request_id?(headers)
28
+ return false unless headers.respond_to?(:each_pair)
29
+
30
+ headers.any? { |k, _| k.to_s.casecmp?(HEADER) }
31
+ end
32
+
33
+ # Whether the given query parameters already carry an x-algolia-request-id
34
+ # entry, whatever its casing and key type. The server consults the query
35
+ # parameter only when the header is absent, so minting a header would
36
+ # override a caller-supplied value sent on that channel.
37
+ #
38
+ # @param query_params [Hash, nil]
39
+ #
40
+ # @return [true, false]
41
+ #
42
+ def self.request_id_query_param?(query_params)
43
+ return false unless query_params.respond_to?(:each_pair)
44
+
45
+ query_params.any? { |k, _| k.to_s.casecmp?(QUERY_PARAM) }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -23,13 +23,25 @@ module Algolia
23
23
 
24
24
  # @param config [Configuration]
25
25
  # @param requester [Object] requester used for sending requests. Uses Algolia::Http::HttpRequester by default
26
+ # @param request_id_support [true, false] the generated per-client Request-ID capability
26
27
  #
27
- def initialize(config, requester)
28
+ def initialize(config, requester, request_id_support: false)
28
29
  @config = config
29
30
  @requester = requester
31
+ @request_id_support = request_id_support
30
32
  @retry_strategy = RetryStrategy.new(config.hosts)
31
33
  end
32
34
 
35
+ # Whether the transport mints Request-ID headers. Resolved per request so a
36
+ # caller can flip config.request_id_enabled at any time, like every other
37
+ # Configuration setting; nil falls back to the per-client capability.
38
+ #
39
+ # @return [true, false]
40
+ #
41
+ def request_id_enabled?
42
+ @config.request_id_enabled.nil? ? @request_id_support : @config.request_id_enabled
43
+ end
44
+
33
45
  # @param call_type [Binary] READ or WRITE operation
34
46
  # @param method [Symbol] method used for request
35
47
  # @param path [String] path of the request
@@ -41,7 +53,23 @@ module Algolia
41
53
  def request(call_type, method, path, body, opts = {})
42
54
  retry_errors = []
43
55
 
56
+ # The Request-ID is minted once per execution, before the host loop, so that
57
+ # every retry attempt shares the same value and each subsequent call gets a
58
+ # fresh one.
59
+ request_id = mint_request_id(opts)
60
+
61
+ # create consumes these two channels from opts on every attempt: snapshot them
62
+ # so retries keep them. Timeout keys stay consumable, recomputed per host below.
63
+ header_params = opts[:header_params]
64
+ query_params = opts[:query_params]
65
+
66
+ # The Correlation-ID of the last retried attempt whose response carried a
67
+ # non-empty one, surfaced on the exhaustion error for support tickets.
68
+ last_correlation_id = nil
69
+
44
70
  @retry_strategy.get_tryable_hosts(call_type).each do |host|
71
+ opts[:header_params] ||= header_params
72
+ opts[:query_params] ||= query_params
45
73
  opts[:timeout] ||= get_timeout(call_type)
46
74
  opts[:connect_timeout] ||= (@config.connect_timeout || Defaults::CONNECT_TIMEOUT) * (host.retry_count + 1)
47
75
 
@@ -50,7 +78,7 @@ module Algolia
50
78
  # TODO: what is this merge for ?
51
79
  # request_options.query_params.merge!(request_options.data) if method == :GET
52
80
 
53
- request = build_request(method, path, body, request_options)
81
+ request = build_request(method, path, body, request_options, request_id)
54
82
  response = @requester.send_request(
55
83
  host,
56
84
  request[:method],
@@ -69,16 +97,20 @@ module Algolia
69
97
  network_failure: response.network_failure
70
98
  )
71
99
  if outcome == FAILURE
100
+ correlation_id = correlation_id_from(response.headers)
101
+
72
102
  # handle HTML error
73
103
  if response.headers["content-type"]&.include?("text/html")
74
- raise Algolia::AlgoliaHttpError.new(response.status, response.reason_phrase)
104
+ raise Algolia::AlgoliaHttpError.new(response.status, response.reason_phrase, correlation_id)
75
105
  end
76
106
 
77
107
  decoded_error = JSON.parse(response.error, :symbolize_names => true)
78
- raise Algolia::AlgoliaHttpError.new(response.status, decoded_error[:message])
108
+ raise Algolia::AlgoliaHttpError.new(response.status, decoded_error[:message], correlation_id)
79
109
  end
80
110
 
81
111
  if outcome == RETRY
112
+ correlation_id = correlation_id_from(response.headers)
113
+ last_correlation_id = correlation_id unless correlation_id.nil? || correlation_id.empty?
82
114
  retry_errors << {host: host.url, error: response.error}
83
115
  else
84
116
  return response
@@ -88,29 +120,65 @@ module Algolia
88
120
  raise(
89
121
  Algolia::AlgoliaUnreachableHostError.new(
90
122
  "Unreachable hosts. If the error persists, please visit our help center https://alg.li/support-unreachable-hosts or reach out to the Algolia Support team: https://alg.li/support",
91
- retry_errors
123
+ retry_errors,
124
+ last_correlation_id
92
125
  )
93
126
  )
94
127
  end
95
128
 
96
129
  private
97
130
 
131
+ # Returns a fresh Request-ID, or nil when the feature is off for this client
132
+ # or the caller already supplied one through the request options, the config
133
+ # default headers, or the x-algolia-request-id query parameter. Caller-supplied
134
+ # values ride their own channel on every attempt.
135
+ #
136
+ # @param opts [Hash]
137
+ #
138
+ # @return [String, nil]
139
+ #
140
+ def mint_request_id(opts)
141
+ return nil unless request_id_enabled?
142
+ return nil if RequestId.request_id?(opts[:header_params])
143
+ return nil if RequestId.request_id?(@config.header_params)
144
+ return nil if RequestId.request_id_query_param?(opts[:query_params])
145
+
146
+ RequestId.generate
147
+ end
148
+
149
+ # Reads the Correlation-ID header of a failed response case-insensitively: the
150
+ # headers hash keeps the server's casing and defaults to an empty string on
151
+ # timeout and network failures. The unrelated X-Algolia-RequestID edge header
152
+ # must never be read instead.
153
+ #
154
+ # @param headers [Hash, String]
155
+ #
156
+ # @return [String, nil]
157
+ #
158
+ def correlation_id_from(headers)
159
+ return nil unless headers.respond_to?(:each_pair)
160
+
161
+ headers.find { |k, _| k.to_s.casecmp?(RequestId::CORRELATION_HEADER) }&.last
162
+ end
163
+
98
164
  # Parse the different information and build the request
99
165
  #
100
166
  # @param [Symbol] method
101
167
  # @param [String] path
102
168
  # @param [Hash] body
103
169
  # @param [RequestOptions] request_options
170
+ # @param [String, nil] request_id
104
171
  #
105
172
  # @return [Hash]
106
173
  #
107
- def build_request(method, path, body, request_options)
174
+ def build_request(method, path, body, request_options, request_id = nil)
108
175
  request = {}
109
176
  request[:method] = method.downcase
110
177
  request[:path] = path
111
178
  request[:body] = build_body(body, request_options)
112
179
  request[:query_params] = Algolia::Transport.stringify_query_params(request_options.query_params)
113
- request[:header_params] = generate_header_params(body, request_options)
180
+
181
+ request[:header_params] = generate_header_params(body, request_options, request_id)
114
182
  request[:timeout] = request_options.timeout
115
183
  request[:connect_timeout] = request_options.connect_timeout
116
184
  request
@@ -129,13 +197,16 @@ module Algolia
129
197
 
130
198
  # Generates headers from config headers and optional parameters
131
199
  #
200
+ # @param body [String, nil]
132
201
  # @param request_options [RequestOptions]
202
+ # @param request_id [String, nil]
133
203
  #
134
204
  # @return [Hash] merged headers
135
205
  #
136
- def generate_header_params(body, request_options)
206
+ def generate_header_params(body, request_options, request_id = nil)
137
207
  header_params = request_options.header_params.transform_keys(&:downcase)
138
208
  header_params = @config.header_params.merge(header_params)
209
+ header_params[RequestId::HEADER] = request_id if request_id
139
210
  if request_options.compression_type == "gzip" && body.is_a?(String) && !body.to_s.strip.empty?
140
211
  header_params["content-encoding"] = "gzip"
141
212
  end
@@ -3,5 +3,5 @@
3
3
  # Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT.
4
4
 
5
5
  module Algolia
6
- VERSION = "3.43.0"
6
+ VERSION = "3.45.0"
7
7
  end
data/lib/algolia.rb CHANGED
@@ -17,6 +17,7 @@ require "algolia/transport/call_type"
17
17
  require "algolia/transport/retry_outcome_type"
18
18
  require "algolia/transport/stateful_host"
19
19
  require "algolia/transport/retry_strategy"
20
+ require "algolia/transport/request_id"
20
21
  require "algolia/transport/request_options"
21
22
  require "algolia/transport/transport"
22
23
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: algolia
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.43.0
4
+ version: 3.45.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://alg.li/support
@@ -1010,6 +1010,7 @@ files:
1010
1010
  - lib/algolia/transport/echo_requester.rb
1011
1011
  - lib/algolia/transport/http/http_requester.rb
1012
1012
  - lib/algolia/transport/http/response.rb
1013
+ - lib/algolia/transport/request_id.rb
1013
1014
  - lib/algolia/transport/request_options.rb
1014
1015
  - lib/algolia/transport/retry_outcome_type.rb
1015
1016
  - lib/algolia/transport/retry_strategy.rb