algolia 3.43.0 → 3.46.1

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.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/issue.yml +6 -4
  3. data/.github/workflows/release.yml +3 -3
  4. data/AGENTS.md +0 -24
  5. data/CHANGELOG.md +50 -0
  6. data/Gemfile.lock +1 -1
  7. data/lib/algolia/api/abtesting_client.rb +20 -13
  8. data/lib/algolia/api/abtesting_v3_client.rb +198 -8
  9. data/lib/algolia/api/agent_studio_client.rb +246 -7
  10. data/lib/algolia/api/analytics_client.rb +12 -11
  11. data/lib/algolia/api/composition_client.rb +2 -1
  12. data/lib/algolia/api/ingestion_client.rb +2 -1
  13. data/lib/algolia/api/insights_client.rb +2 -1
  14. data/lib/algolia/api/monitoring_client.rb +2 -1
  15. data/lib/algolia/api/personalization_client.rb +2 -1
  16. data/lib/algolia/api/query_suggestions_client.rb +2 -1
  17. data/lib/algolia/api/recommend_client.rb +4 -3
  18. data/lib/algolia/api/search_client.rb +141 -23
  19. data/lib/algolia/api_client.rb +10 -2
  20. data/lib/algolia/chunked_helper_options.rb +1 -0
  21. data/lib/algolia/configuration.rb +18 -2
  22. data/lib/algolia/defaults.rb +8 -0
  23. data/lib/algolia/error.rb +20 -3
  24. data/lib/algolia/models/abtesting-v3/ab_test.rb +13 -11
  25. data/lib/algolia/models/abtesting-v3/ab_test_configuration.rb +22 -4
  26. data/lib/algolia/models/abtesting-v3/ab_test_settings_response.rb +224 -0
  27. data/lib/algolia/models/abtesting-v3/add_ab_tests_request.rb +11 -1
  28. data/lib/algolia/models/abtesting-v3/analysis_method.rb +34 -0
  29. data/lib/algolia/models/abtesting-v3/bayesian_metric_result.rb +237 -0
  30. data/lib/algolia/models/abtesting-v3/evidence_status.rb +37 -0
  31. data/lib/algolia/models/abtesting-v3/feature_settings.rb +224 -0
  32. data/lib/algolia/models/abtesting-v3/metric_evidence.rb +209 -0
  33. data/lib/algolia/models/abtesting-v3/metric_metadata.rb +1 -1
  34. data/lib/algolia/models/abtesting-v3/metric_name.rb +7 -1
  35. data/lib/algolia/models/abtesting-v3/metric_result.rb +18 -8
  36. data/lib/algolia/models/abtesting-v3/minimum_detectable_effect.rb +1 -1
  37. data/lib/algolia/models/abtesting-v3/primary_metric.rb +37 -0
  38. data/lib/algolia/models/abtesting-v3/save_settings_request.rb +208 -0
  39. data/lib/algolia/models/abtesting-v3/variant_settings.rb +293 -0
  40. data/lib/algolia/models/agent-studio/agent_completion_algolia_params.rb +16 -4
  41. data/lib/algolia/models/agent-studio/agent_completion_request.rb +1 -3
  42. data/lib/algolia/models/agent-studio/agent_completion_request_union.rb +108 -0
  43. data/lib/algolia/models/agent-studio/agent_test_configuration.rb +1 -1
  44. data/lib/algolia/models/agent-studio/agent_with_version_response.rb +1 -1
  45. data/lib/algolia/models/agent-studio/agui_completion_request.rb +265 -0
  46. data/lib/algolia/models/agent-studio/agui_resume.rb +231 -0
  47. data/lib/algolia/models/agent-studio/algolia_grouped_results_compat_tool_config.rb +268 -0
  48. data/lib/algolia/models/agent-studio/{algolia_display_results_tool_config.rb → algolia_grouped_results_tool_config.rb} +30 -16
  49. data/lib/algolia/models/agent-studio/{algolia_recommend_tool_config_input.rb → algolia_recommend_tool_config.rb} +3 -3
  50. data/lib/algolia/models/agent-studio/algolia_search_tool_config.rb +22 -4
  51. data/lib/algolia/models/agent-studio/assistant_message_agui.rb +280 -0
  52. data/lib/algolia/models/agent-studio/assistant_message_v4.rb +1 -0
  53. data/lib/algolia/models/agent-studio/assistant_message_v5.rb +3 -1
  54. data/lib/algolia/models/agent-studio/assistant_part_v5.rb +2 -0
  55. data/lib/algolia/models/agent-studio/azure_open_ai_provider_input.rb +1 -1
  56. data/lib/algolia/models/agent-studio/boolean_param.rb +220 -0
  57. data/lib/algolia/models/agent-studio/client_side_tool_config.rb +14 -4
  58. data/lib/algolia/models/agent-studio/compaction_stats.rb +271 -0
  59. data/lib/algolia/models/agent-studio/compatibility_mode.rb +2 -1
  60. data/lib/algolia/models/agent-studio/context_compact_request.rb +263 -0
  61. data/lib/algolia/models/agent-studio/context_response.rb +231 -0
  62. data/lib/algolia/models/agent-studio/context_stats.rb +258 -0
  63. data/lib/algolia/models/agent-studio/context_trim_request.rb +239 -0
  64. data/lib/algolia/models/agent-studio/conversation_base_response.rb +16 -5
  65. data/lib/algolia/models/agent-studio/conversation_full_response.rb +26 -5
  66. data/lib/algolia/models/agent-studio/data_guardrail_violation_part_v5.rb +218 -0
  67. data/lib/algolia/models/agent-studio/data_part_v5.rb +220 -0
  68. data/lib/algolia/models/agent-studio/facet_filters_union_search_parameters_overrides.rb +108 -0
  69. data/lib/algolia/models/agent-studio/feedback_update_request.rb +253 -0
  70. data/lib/algolia/models/agent-studio/forwarded_props.rb +226 -0
  71. data/lib/algolia/models/agent-studio/function_call_agui.rb +219 -0
  72. data/lib/algolia/models/agent-studio/guardrail_outcome.rb +231 -0
  73. data/lib/algolia/models/agent-studio/guardrail_violation_data_v5.rb +230 -0
  74. data/lib/algolia/models/agent-studio/impact_analytics.rb +242 -0
  75. data/lib/algolia/models/agent-studio/index_search_parameters.rb +11 -1
  76. data/lib/algolia/models/agent-studio/input_union.rb +113 -0
  77. data/lib/algolia/models/agent-studio/items_union.rb +132 -0
  78. data/lib/algolia/models/agent-studio/items_union_agui_completion_request.rb +123 -0
  79. data/lib/algolia/models/agent-studio/managed_provider_response_input.rb +209 -0
  80. data/lib/algolia/models/agent-studio/mcp_server_tool_config.rb +1 -1
  81. data/lib/algolia/models/agent-studio/memory_record.rb +4 -4
  82. data/lib/algolia/models/agent-studio/message_event.rb +231 -0
  83. data/lib/algolia/models/agent-studio/message_part_assistant_message_agui.rb +123 -0
  84. data/lib/algolia/models/agent-studio/message_response.rb +87 -5
  85. data/lib/algolia/models/agent-studio/mode_enum.rb +34 -0
  86. data/lib/algolia/models/agent-studio/one_of_enum.rb +34 -0
  87. data/lib/algolia/models/agent-studio/open_ai_compatible_provider_input.rb +1 -1
  88. data/lib/algolia/models/agent-studio/paginated_conversations_response.rb +13 -4
  89. data/lib/algolia/models/agent-studio/provider_authentication_response.rb +14 -5
  90. data/lib/algolia/models/agent-studio/provider_input.rb +2 -1
  91. data/lib/algolia/models/agent-studio/provider_input_nullable.rb +2 -1
  92. data/lib/algolia/models/agent-studio/provider_name.rb +2 -1
  93. data/lib/algolia/models/agent-studio/reasoning_message_agui.rb +239 -0
  94. data/lib/algolia/models/agent-studio/reasoning_part_agui.rb +228 -0
  95. data/lib/algolia/models/agent-studio/search_parameters.rb +1 -1
  96. data/lib/algolia/models/agent-studio/search_parameters_overrides.rb +80 -5
  97. data/lib/algolia/models/agent-studio/status_enum.rb +34 -0
  98. data/lib/algolia/models/agent-studio/task_kind.rb +33 -0
  99. data/lib/algolia/models/agent-studio/task_request.rb +240 -0
  100. data/lib/algolia/models/agent-studio/task_response.rb +211 -0
  101. data/lib/algolia/models/agent-studio/text_part_agui.rb +219 -0
  102. data/lib/algolia/models/agent-studio/tool_approval_agui.rb +228 -0
  103. data/lib/algolia/models/agent-studio/tool_approval_request_part.rb +15 -5
  104. data/lib/algolia/models/agent-studio/tool_call_agui.rb +237 -0
  105. data/lib/algolia/models/agent-studio/tool_call_part.rb +13 -1
  106. data/lib/algolia/models/agent-studio/tool_call_part_agui.rb +272 -0
  107. data/lib/algolia/models/agent-studio/tool_call_result_part_agui.rb +250 -0
  108. data/lib/algolia/models/agent-studio/tool_config_input.rb +2 -5
  109. data/lib/algolia/models/agent-studio/{tool_config.rb → tool_config_or_boolean_union.rb} +1 -1
  110. data/lib/algolia/models/agent-studio/tool_config_output.rb +126 -0
  111. data/lib/algolia/models/agent-studio/tool_invocation_v4.rb +10 -2
  112. data/lib/algolia/models/agent-studio/tool_message_agui.rb +259 -0
  113. data/lib/algolia/models/agent-studio/tool_part_v5.rb +47 -2
  114. data/lib/algolia/models/agent-studio/tool_result_part.rb +57 -5
  115. data/lib/algolia/models/agent-studio/unknown_tool_config.rb +1 -1
  116. data/lib/algolia/models/agent-studio/user_message_agui.rb +261 -0
  117. data/lib/algolia/models/agent-studio/user_message_metadata_agui.rb +208 -0
  118. data/lib/algolia/models/agent-studio/user_message_metadata_v5.rb +4 -2
  119. data/lib/algolia/models/agent-studio/user_message_v4.rb +4 -1
  120. data/lib/algolia/models/agent-studio/user_message_v5.rb +4 -1
  121. data/lib/algolia/models/agent-studio/validation_error.rb +5 -25
  122. data/lib/algolia/models/agent-studio/xai_provider_input.rb +220 -0
  123. data/lib/algolia/models/analytics/catalog_entry.rb +1 -1
  124. data/lib/algolia/models/composition/base_injection_query_parameters.rb +18 -18
  125. data/lib/algolia/models/composition/distinct.rb +1 -1
  126. data/lib/algolia/models/composition/external_provider.rb +209 -0
  127. data/lib/algolia/models/composition/external_provider_ordering.rb +34 -0
  128. data/lib/algolia/models/composition/hit_metadata.rb +2 -2
  129. data/lib/algolia/models/composition/injected_item_external.rb +21 -14
  130. data/lib/algolia/models/composition/injected_item_external_provider.rb +258 -0
  131. data/lib/algolia/models/composition/injected_item_external_provider_source.rb +216 -0
  132. data/lib/algolia/models/composition/injected_item_hits_metadata.rb +1 -1
  133. data/lib/algolia/models/composition/{recommend.rb → injected_item_recommend.rb} +10 -3
  134. data/lib/algolia/models/composition/injected_item_recommend_source.rb +1 -1
  135. data/lib/algolia/models/composition/injected_item_search.rb +8 -1
  136. data/lib/algolia/models/composition/injected_item_source.rb +1 -0
  137. data/lib/algolia/models/composition/injection_main_external_provider.rb +258 -0
  138. data/lib/algolia/models/composition/injection_main_external_provider_source.rb +216 -0
  139. data/lib/algolia/models/composition/{main_recommend.rb → injection_main_recommend.rb} +10 -3
  140. data/lib/algolia/models/composition/injection_main_recommend_source.rb +1 -1
  141. data/lib/algolia/models/composition/{main_search.rb → injection_main_search.rb} +11 -4
  142. data/lib/algolia/models/composition/injection_main_search_source.rb +1 -1
  143. data/lib/algolia/models/composition/injection_main_source.rb +1 -0
  144. data/lib/algolia/models/composition/main_injection_query_parameters.rb +22 -22
  145. data/lib/algolia/models/composition/optional_words.rb +1 -1
  146. data/lib/algolia/models/composition/params.rb +6 -6
  147. data/lib/algolia/models/composition/processing_error.rb +209 -0
  148. data/lib/algolia/models/composition/request_body.rb +13 -4
  149. data/lib/algolia/models/composition/result_card.rb +208 -0
  150. data/lib/algolia/models/composition/search_response.rb +16 -4
  151. data/lib/algolia/models/composition/widgets.rb +13 -4
  152. data/lib/algolia/models/ingestion/source_update_commercetools.rb +11 -0
  153. data/lib/algolia/models/recommend/distinct.rb +1 -1
  154. data/lib/algolia/models/recommend/fallback_params.rb +26 -26
  155. data/lib/algolia/models/recommend/optional_words.rb +1 -1
  156. data/lib/algolia/models/recommend/recommend_search_params.rb +26 -26
  157. data/lib/algolia/models/recommend/result_card.rb +208 -0
  158. data/lib/algolia/models/recommend/widgets.rb +13 -4
  159. data/lib/algolia/models/search/api_key.rb +2 -2
  160. data/lib/algolia/models/search/attribute_criteria_computed_by.rb +35 -0
  161. data/lib/algolia/models/search/browse_params_object.rb +35 -25
  162. data/lib/algolia/models/search/consequence_params.rb +35 -25
  163. data/lib/algolia/models/search/distinct.rb +1 -1
  164. data/lib/algolia/models/search/get_api_key_response.rb +2 -2
  165. data/lib/algolia/models/search/index_settings.rb +28 -18
  166. data/lib/algolia/models/search/log.rb +11 -0
  167. data/lib/algolia/models/search/optional_words.rb +1 -1
  168. data/lib/algolia/models/search/result_card.rb +208 -0
  169. data/lib/algolia/models/search/search_for_facets.rb +35 -25
  170. data/lib/algolia/models/search/search_for_hits.rb +35 -25
  171. data/lib/algolia/models/search/search_params_object.rb +35 -25
  172. data/lib/algolia/models/search/secured_api_key_restrictions.rb +1 -1
  173. data/lib/algolia/models/search/settings_response.rb +28 -18
  174. data/lib/algolia/models/search/widgets.rb +13 -4
  175. data/lib/algolia/transport/http/http_requester.rb +2 -2
  176. data/lib/algolia/transport/request_id.rb +49 -0
  177. data/lib/algolia/transport/transport.rb +161 -18
  178. data/lib/algolia/version.rb +1 -1
  179. data/lib/algolia.rb +1 -0
  180. metadata +73 -8
@@ -9,7 +9,7 @@ module Algolia
9
9
  # Configuration options for the ingestion transporter used by *_with_transformation helpers.
10
10
  # When passed to SearchClient.with_transformation or set via set_transformation_options,
11
11
  # the ingestion transporter is eagerly created using Ingestion API defaults (25s timeouts,
12
- # no compression). Only fields explicitly set here override those defaults.
12
+ # no compression, 3 waits on HTTP 429). Only fields explicitly set here override those defaults.
13
13
  # See https://www.algolia.com/doc/libraries/ruby/v3/methods/ingestion
14
14
  class TransformationOptions
15
15
  attr_accessor(
@@ -19,7 +19,8 @@ module Algolia
19
19
  :write_timeout,
20
20
  :hosts,
21
21
  :compression_type,
22
- :header_params
22
+ :header_params,
23
+ :max_rate_limit_retries
23
24
  )
24
25
 
25
26
  def initialize(region, opts = {})
@@ -30,7 +31,15 @@ module Algolia
30
31
  )
31
32
  end
32
33
 
33
- valid_keys = %i[connect_timeout read_timeout write_timeout hosts compression_type header_params]
34
+ valid_keys = %i[
35
+ connect_timeout
36
+ read_timeout
37
+ write_timeout
38
+ hosts
39
+ compression_type
40
+ header_params
41
+ max_rate_limit_retries
42
+ ]
34
43
  unknown = opts.keys - valid_keys
35
44
  unless unknown.empty?
36
45
  raise(
@@ -46,6 +55,7 @@ module Algolia
46
55
  @hosts = opts[:hosts]
47
56
  @compression_type = opts[:compression_type]
48
57
  @header_params = opts[:header_params]
58
+ @max_rate_limit_retries = opts[:max_rate_limit_retries]
49
59
  end
50
60
  end
51
61
 
@@ -69,7 +79,8 @@ module Algolia
69
79
  config.write_timeout = 30000
70
80
  end
71
81
 
72
- @api_client = Algolia::ApiClient.new(config)
82
+ # Per-client capability; an explicit config.request_id_enabled always wins.
83
+ @api_client = Algolia::ApiClient.new(config, request_id_support: true)
73
84
  @ingestion_transporter = nil
74
85
  if config.transformation_options
75
86
  @ingestion_transporter = _build_ingestion_transporter(config.transformation_options)
@@ -3412,6 +3423,42 @@ module Algolia
3412
3423
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Search::UpdateApiKeyResponse")
3413
3424
  end
3414
3425
 
3426
+ # Helper: Derives the request options carrying the Request-ID shared by every request
3427
+ # of one helper invocation. Returns the options untouched when the client does not
3428
+ # support Request-ID or the caller already supplied one through the options (as a
3429
+ # header or an x-algolia-request-id query parameter) or the config headers, which
3430
+ # also makes nested helpers reuse the ID minted by their caller.
3431
+ #
3432
+ # @param request_options [Hash]
3433
+ #
3434
+ # @return [Hash]
3435
+ private def with_request_id(request_options = {})
3436
+ config = api_client.config
3437
+ unless api_client.request_id_enabled? &&
3438
+ !Transport::RequestId.request_id?(request_options[:header_params]) &&
3439
+ !Transport::RequestId.request_id?(config.header_params) &&
3440
+ !Transport::RequestId.request_id_query_param?(request_options[:query_params])
3441
+ return request_options
3442
+ end
3443
+
3444
+ request_options.merge(
3445
+ :header_params => (request_options[:header_params] || {}).merge(
3446
+ Transport::RequestId::HEADER => Transport::RequestId.generate
3447
+ )
3448
+ )
3449
+ end
3450
+
3451
+ # Helper: Drops the caller's timeouts from the given request options, so that the
3452
+ # failure-path cleanup calls run with the client defaults while keeping the other
3453
+ # options, notably the headers carrying the invocation's shared Request-ID.
3454
+ #
3455
+ # @param request_options [Hash]
3456
+ #
3457
+ # @return [Hash]
3458
+ private def without_timeouts(request_options)
3459
+ request_options.reject { |key, _| key == :timeout || key == :connect_timeout }
3460
+ end
3461
+
3415
3462
  # The parent search config MUST NOT leak into the ingestion transporter.
3416
3463
  def _build_ingestion_transporter(transformation_options)
3417
3464
  hosts = if transformation_options.hosts
@@ -3436,6 +3483,10 @@ module Algolia
3436
3483
  opts[:compression_type] = transformation_options.compression_type
3437
3484
  end
3438
3485
 
3486
+ unless transformation_options.max_rate_limit_retries.nil?
3487
+ opts[:max_rate_limit_retries] = transformation_options.max_rate_limit_retries
3488
+ end
3489
+
3439
3490
  config = Algolia::Configuration.new(
3440
3491
  @api_client.config.app_id,
3441
3492
  @api_client.config.api_key,
@@ -3561,6 +3612,10 @@ module Algolia
3561
3612
  )
3562
3613
  assert_ingestion_transporter!
3563
3614
 
3615
+ # The shared Request-ID only covers the search-side calls: the ingestion
3616
+ # push goes to an API that must not receive the header.
3617
+ search_request_options = with_request_id(request_options)
3618
+
3564
3619
  if objects.empty?
3565
3620
  @api_client.logger.warn(
3566
3621
  "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 +3636,7 @@ module Algolia
3581
3636
  destination: tmp_index_name,
3582
3637
  scope: scopes
3583
3638
  ),
3584
- request_options
3639
+ search_request_options
3585
3640
  )
3586
3641
 
3587
3642
  watch_responses = @ingestion_transporter.chunked_push(
@@ -3595,7 +3650,13 @@ module Algolia
3595
3650
  opts
3596
3651
  )
3597
3652
 
3598
- wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
3653
+ wait_for_task(
3654
+ tmp_index_name,
3655
+ copy_operation_response.task_id,
3656
+ opts.max_retries,
3657
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3658
+ search_request_options
3659
+ )
3599
3660
 
3600
3661
  copy_operation_response = operation_index(
3601
3662
  index_name,
@@ -3604,10 +3665,16 @@ module Algolia
3604
3665
  destination: tmp_index_name,
3605
3666
  scope: scopes
3606
3667
  ),
3607
- request_options
3668
+ search_request_options
3608
3669
  )
3609
3670
 
3610
- wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
3671
+ wait_for_task(
3672
+ tmp_index_name,
3673
+ copy_operation_response.task_id,
3674
+ opts.max_retries,
3675
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3676
+ search_request_options
3677
+ )
3611
3678
 
3612
3679
  move_operation_response = operation_index(
3613
3680
  tmp_index_name,
@@ -3615,10 +3682,16 @@ module Algolia
3615
3682
  operation: Search::OperationType::MOVE,
3616
3683
  destination: index_name
3617
3684
  ),
3618
- request_options
3685
+ search_request_options
3619
3686
  )
3620
3687
 
3621
- wait_for_task(tmp_index_name, move_operation_response.task_id, opts.max_retries)
3688
+ wait_for_task(
3689
+ tmp_index_name,
3690
+ move_operation_response.task_id,
3691
+ opts.max_retries,
3692
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3693
+ search_request_options
3694
+ )
3622
3695
 
3623
3696
  search_watch_responses = watch_responses.map do |wr|
3624
3697
  Search::WatchResponse.build_from_hash(wr.to_hash)
@@ -3630,7 +3703,13 @@ module Algolia
3630
3703
  move_operation_response: move_operation_response
3631
3704
  )
3632
3705
  rescue Exception => e
3633
- delete_index(tmp_index_name)
3706
+ begin
3707
+ delete_index(tmp_index_name, without_timeouts(search_request_options))
3708
+ rescue StandardError => cleanup_error
3709
+ @api_client.logger.warn(
3710
+ "Failed to delete the temporary index \"#{tmp_index_name}\", please delete it manually: #{cleanup_error}"
3711
+ )
3712
+ end
3634
3713
 
3635
3714
  raise e
3636
3715
  end
@@ -3641,16 +3720,17 @@ module Algolia
3641
3720
  # @param index_name [String] the `index_name` where the operation was performed. (required)
3642
3721
  # @param task_id [Integer] the `task_id` returned in the method response. (required)
3643
3722
  # @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)
3723
+ # @param timeout [Proc] the function to decide how long to wait between retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT)
3645
3724
  # @param request_options [Hash] the requestOptions to send along with the query, they will be forwarded to the `get_task` method.
3646
3725
  # @return [Http::Response] the last get_task response
3647
3726
  def wait_for_task(
3648
3727
  index_name,
3649
3728
  task_id,
3650
3729
  max_retries = Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES,
3651
- timeout = -> (retry_count) { [retry_count * 200, 5000].min },
3730
+ timeout = Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3652
3731
  request_options = {}
3653
3732
  )
3733
+ request_options = with_request_id(request_options)
3654
3734
  retries = 0
3655
3735
  while retries < max_retries
3656
3736
  res = get_task(index_name, task_id, request_options)
@@ -3672,15 +3752,16 @@ module Algolia
3672
3752
  #
3673
3753
  # @param task_id [Integer] the `task_id` returned in the method response. (required)
3674
3754
  # @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)
3755
+ # @param timeout [Proc] the function to decide how long to wait between retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT)
3676
3756
  # @param request_options [Hash] the requestOptions to send along with the query, they will be forwarded to the `get_task` method.
3677
3757
  # @return [Http::Response] the last get_task response
3678
3758
  def wait_for_app_task(
3679
3759
  task_id,
3680
3760
  max_retries = Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES,
3681
- timeout = -> (retry_count) { [retry_count * 200, 5000].min },
3761
+ timeout = Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3682
3762
  request_options = {}
3683
3763
  )
3764
+ request_options = with_request_id(request_options)
3684
3765
  retries = 0
3685
3766
  while retries < max_retries
3686
3767
  res = get_app_task(task_id, request_options)
@@ -3703,8 +3784,8 @@ module Algolia
3703
3784
  # @param key [String] the `key` that has been added, deleted or updated.
3704
3785
  # @param operation [String] the `operation` that was done on a `key`.
3705
3786
  # @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.
3787
+ # @param max_retries [Integer] the maximum number of retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES)
3788
+ # @param timeout [Proc] the function to decide how long to wait between retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT)
3708
3789
  # @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
3790
  # @return [Http::Response] the last get_api_key response
3710
3791
  def wait_for_api_key(
@@ -3712,9 +3793,10 @@ module Algolia
3712
3793
  operation,
3713
3794
  api_key = Search::ApiKey.new,
3714
3795
  max_retries = Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES,
3715
- timeout = -> (retry_count) { [retry_count * 200, 5000].min },
3796
+ timeout = Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
3716
3797
  request_options = {}
3717
3798
  )
3799
+ request_options = with_request_id(request_options)
3718
3800
  api_key = api_client.object_to_hash(api_key)
3719
3801
 
3720
3802
  retries = 0
@@ -3769,6 +3851,7 @@ module Algolia
3769
3851
  # @param request_options [Hash] the requestOptions to send along with the query, they will be forwarded to the `browse` method.
3770
3852
  # @param block [Proc] the block to execute on each object of the index.
3771
3853
  def browse_objects(index_name, browse_params = Search::BrowseParamsObject.new, request_options = {}, &block)
3854
+ request_options = with_request_id(request_options)
3772
3855
  browse_params = api_client.object_to_hash(browse_params)
3773
3856
 
3774
3857
  browse_params[:hitsPerPage] = 1000 unless browse_params.key?(:hitsPerPage)
@@ -3798,6 +3881,7 @@ module Algolia
3798
3881
  # @param request_options [Hash] the requestOptions to send along with the query, they will be forwarded to the `searchRules` method.
3799
3882
  # @param block [Proc] the block to execute on each rule of the index.
3800
3883
  def browse_rules(index_name, search_rules_params = Search::SearchRulesParams.new, request_options = {}, &block)
3884
+ request_options = with_request_id(request_options)
3801
3885
  search_rules_params = api_client.object_to_hash(search_rules_params)
3802
3886
 
3803
3887
  search_rules_params[:page] ||= 0
@@ -3833,6 +3917,7 @@ module Algolia
3833
3917
  request_options = {},
3834
3918
  &block
3835
3919
  )
3920
+ request_options = with_request_id(request_options)
3836
3921
  search_synonyms_params = api_client.object_to_hash(search_synonyms_params)
3837
3922
 
3838
3923
  search_synonyms_params[:page] ||= 0
@@ -4036,6 +4121,7 @@ module Algolia
4036
4121
  request_options = {},
4037
4122
  chunked_options = nil
4038
4123
  )
4124
+ request_options = with_request_id(request_options)
4039
4125
  opts = Algolia::ChunkedHelperOptions.resolve(chunked_options)
4040
4126
  responses = []
4041
4127
  objects.each_slice(batch_size) do |chunk|
@@ -4048,7 +4134,13 @@ module Algolia
4048
4134
 
4049
4135
  if wait_for_tasks
4050
4136
  responses.each do |response|
4051
- wait_for_task(index_name, response.task_id, opts.max_retries)
4137
+ wait_for_task(
4138
+ index_name,
4139
+ response.task_id,
4140
+ opts.max_retries,
4141
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
4142
+ request_options
4143
+ )
4052
4144
  end
4053
4145
  end
4054
4146
 
@@ -4074,6 +4166,8 @@ module Algolia
4074
4166
  request_options = {},
4075
4167
  chunked_options = nil
4076
4168
  )
4169
+ request_options = with_request_id(request_options)
4170
+
4077
4171
  if objects.empty?
4078
4172
  @api_client.logger.warn(
4079
4173
  "replace_all_objects was called with an empty list of objects, which will delete all records currently in the \"#{index_name}\" index."
@@ -4107,7 +4201,13 @@ module Algolia
4107
4201
  opts
4108
4202
  )
4109
4203
 
4110
- wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
4204
+ wait_for_task(
4205
+ tmp_index_name,
4206
+ copy_operation_response.task_id,
4207
+ opts.max_retries,
4208
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
4209
+ request_options
4210
+ )
4111
4211
 
4112
4212
  copy_operation_response = operation_index(
4113
4213
  index_name,
@@ -4119,7 +4219,13 @@ module Algolia
4119
4219
  request_options
4120
4220
  )
4121
4221
 
4122
- wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
4222
+ wait_for_task(
4223
+ tmp_index_name,
4224
+ copy_operation_response.task_id,
4225
+ opts.max_retries,
4226
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
4227
+ request_options
4228
+ )
4123
4229
 
4124
4230
  move_operation_response = operation_index(
4125
4231
  tmp_index_name,
@@ -4130,7 +4236,13 @@ module Algolia
4130
4236
  request_options
4131
4237
  )
4132
4238
 
4133
- wait_for_task(tmp_index_name, move_operation_response.task_id, opts.max_retries)
4239
+ wait_for_task(
4240
+ tmp_index_name,
4241
+ move_operation_response.task_id,
4242
+ opts.max_retries,
4243
+ Algolia::ChunkedHelperOptions::DEFAULT_TIMEOUT,
4244
+ request_options
4245
+ )
4134
4246
 
4135
4247
  Search::ReplaceAllObjectsResponse.new(
4136
4248
  copy_operation_response: copy_operation_response,
@@ -4138,7 +4250,13 @@ module Algolia
4138
4250
  move_operation_response: move_operation_response
4139
4251
  )
4140
4252
  rescue Exception => e
4141
- delete_index(tmp_index_name)
4253
+ begin
4254
+ delete_index(tmp_index_name, without_timeouts(request_options))
4255
+ rescue StandardError => cleanup_error
4256
+ @api_client.logger.warn(
4257
+ "Failed to delete the temporary index \"#{tmp_index_name}\", please delete it manually: #{cleanup_error}"
4258
+ )
4259
+ end
4142
4260
 
4143
4261
  raise e
4144
4262
  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,21 @@ 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
+
30
+ # How many times a 429 is waited out on the same host before it is raised.
31
+ # nil means Defaults::MAX_RATE_LIMIT_RETRIES (3); 0 fails on the first 429
32
+ # without waiting; a negative value behaves like 0. The wait is the Retry-After
33
+ # header in whole seconds, or 1 second when it is missing or invalid.
34
+ # @return [Integer, nil]
35
+ attr_accessor :max_rate_limit_retries
36
+
24
37
  def initialize(app_id, api_key, hosts, client_name, opts = {})
25
38
  @hosts = hosts
26
39
  @app_id = app_id
@@ -33,6 +46,9 @@ module Algolia
33
46
  @requester = opts[:requester]
34
47
  @transformation_options = opts[:transformation_options]
35
48
 
49
+ @request_id_enabled = opts[:request_id_enabled]
50
+ @max_rate_limit_retries = opts[:max_rate_limit_retries]
51
+
36
52
  @user_agent = UserAgent.new.add(client_name, VERSION)
37
53
 
38
54
  if opts[:user_agent_segments]
@@ -4,5 +4,13 @@ module Algolia
4
4
  READ_TIMEOUT = 5000
5
5
  WRITE_TIMEOUT = 30000
6
6
  CONNECT_TIMEOUT = 2000
7
+
8
+ # How many times a 429 is waited out on the same host before it is raised.
9
+ MAX_RATE_LIMIT_RETRIES = 3
10
+ # Seconds waited before a 429 retry when Retry-After is missing or invalid.
11
+ RATE_LIMIT_WAIT = 1
12
+ # Largest wait Kernel#sleep accepts (a 64-bit long, in seconds). A runtime
13
+ # ceiling like the Go and Swift clients', not a client-side maximum wait.
14
+ MAX_RATE_LIMIT_WAIT = 2 ** 63 - 1
7
15
  end
8
16
  end
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
@@ -26,6 +26,9 @@ module Algolia
26
26
  # A/B test name.
27
27
  attr_accessor :name
28
28
 
29
+ # Expected outcome of the A/B test.
30
+ attr_accessor :hypothesis
31
+
29
32
  attr_accessor :status
30
33
 
31
34
  # A/B test variants. The first variant is your _control_ index, typically your production index. All of the additional variants are indexes with changed settings that you want to test against the control.
@@ -33,9 +36,6 @@ module Algolia
33
36
 
34
37
  attr_accessor :configuration
35
38
 
36
- # Unique migrated A/B test identifier.
37
- attr_accessor :migrated_ab_test_id
38
-
39
39
  attr_accessor :decision
40
40
 
41
41
  # Attribute mapping from ruby-style variable name to JSON key.
@@ -47,10 +47,10 @@ module Algolia
47
47
  :end_at => :endAt,
48
48
  :stopped_at => :stoppedAt,
49
49
  :name => :name,
50
+ :hypothesis => :hypothesis,
50
51
  :status => :status,
51
52
  :variants => :variants,
52
53
  :configuration => :configuration,
53
- :migrated_ab_test_id => :migratedAbTestID,
54
54
  :decision => :decision
55
55
  }
56
56
  end
@@ -64,10 +64,10 @@ module Algolia
64
64
  :end_at => :"String",
65
65
  :stopped_at => :"String",
66
66
  :name => :"String",
67
+ :hypothesis => :"String",
67
68
  :status => :"Status",
68
69
  :variants => :"Array<Variant>",
69
70
  :configuration => :"ABTestConfiguration",
70
- :migrated_ab_test_id => :"Integer",
71
71
  :decision => :"Decision"
72
72
  }
73
73
  end
@@ -135,6 +135,12 @@ module Algolia
135
135
  self.name = nil
136
136
  end
137
137
 
138
+ if attributes.key?(:hypothesis)
139
+ self.hypothesis = attributes[:hypothesis]
140
+ else
141
+ self.hypothesis = nil
142
+ end
143
+
138
144
  if attributes.key?(:status)
139
145
  self.status = attributes[:status]
140
146
  else
@@ -153,10 +159,6 @@ module Algolia
153
159
  self.configuration = attributes[:configuration]
154
160
  end
155
161
 
156
- if attributes.key?(:migrated_ab_test_id)
157
- self.migrated_ab_test_id = attributes[:migrated_ab_test_id]
158
- end
159
-
160
162
  if attributes.key?(:decision)
161
163
  self.decision = attributes[:decision]
162
164
  end
@@ -173,10 +175,10 @@ module Algolia
173
175
  end_at == other.end_at &&
174
176
  stopped_at == other.stopped_at &&
175
177
  name == other.name &&
178
+ hypothesis == other.hypothesis &&
176
179
  status == other.status &&
177
180
  variants == other.variants &&
178
181
  configuration == other.configuration &&
179
- migrated_ab_test_id == other.migrated_ab_test_id &&
180
182
  decision == other.decision
181
183
  end
182
184
 
@@ -196,10 +198,10 @@ module Algolia
196
198
  end_at,
197
199
  stopped_at,
198
200
  name,
201
+ hypothesis,
199
202
  status,
200
203
  variants,
201
204
  configuration,
202
- migrated_ab_test_id,
203
205
  decision
204
206
  ].hash
205
207
  end
@@ -16,12 +16,18 @@ module Algolia
16
16
 
17
17
  attr_accessor :error_correction
18
18
 
19
+ attr_accessor :method
20
+
21
+ attr_accessor :primary_metric
22
+
19
23
  # Attribute mapping from ruby-style variable name to JSON key.
20
24
  def self.attribute_map
21
25
  {
22
26
  :minimum_detectable_effect => :minimumDetectableEffect,
23
27
  :filters => :filters,
24
- :error_correction => :errorCorrection
28
+ :error_correction => :errorCorrection,
29
+ :method => :method,
30
+ :primary_metric => :primaryMetric
25
31
  }
26
32
  end
27
33
 
@@ -30,7 +36,9 @@ module Algolia
30
36
  {
31
37
  :minimum_detectable_effect => :"MinimumDetectableEffect",
32
38
  :filters => :"Array<MetricsFilter>",
33
- :error_correction => :"ErrorCorrectionType"
39
+ :error_correction => :"ErrorCorrectionType",
40
+ :method => :"AnalysisMethod",
41
+ :primary_metric => :"PrimaryMetric"
34
42
  }
35
43
  end
36
44
 
@@ -77,6 +85,14 @@ module Algolia
77
85
  if attributes.key?(:error_correction)
78
86
  self.error_correction = attributes[:error_correction]
79
87
  end
88
+
89
+ if attributes.key?(:method)
90
+ self.method = attributes[:method]
91
+ end
92
+
93
+ if attributes.key?(:primary_metric)
94
+ self.primary_metric = attributes[:primary_metric]
95
+ end
80
96
  end
81
97
 
82
98
  # Checks equality by comparing each attribute.
@@ -86,7 +102,9 @@ module Algolia
86
102
  self.class == other.class &&
87
103
  minimum_detectable_effect == other.minimum_detectable_effect &&
88
104
  filters == other.filters &&
89
- error_correction == other.error_correction
105
+ error_correction == other.error_correction &&
106
+ method == other.method &&
107
+ primary_metric == other.primary_metric
90
108
  end
91
109
 
92
110
  # @see the `==` method
@@ -98,7 +116,7 @@ module Algolia
98
116
  # Calculates hash code according to all attributes.
99
117
  # @return [Integer] Hash code
100
118
  def hash
101
- [minimum_detectable_effect, filters, error_correction].hash
119
+ [minimum_detectable_effect, filters, error_correction, method, primary_metric].hash
102
120
  end
103
121
 
104
122
  # Builds the object from hash