mongo 2.20.1 → 2.21.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.
Files changed (246) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/Rakefile +2 -2
  4. data/lib/mongo/address.rb +22 -3
  5. data/lib/mongo/auth/aws/credentials_retriever.rb +70 -17
  6. data/lib/mongo/auth/base.rb +1 -1
  7. data/lib/mongo/bulk_write.rb +35 -2
  8. data/lib/mongo/client.rb +38 -6
  9. data/lib/mongo/client_encryption.rb +6 -3
  10. data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -1
  11. data/lib/mongo/cluster/sdam_flow.rb +20 -7
  12. data/lib/mongo/cluster.rb +14 -4
  13. data/lib/mongo/collection/helpers.rb +1 -1
  14. data/lib/mongo/collection/view/aggregation/behavior.rb +131 -0
  15. data/lib/mongo/collection/view/aggregation.rb +33 -99
  16. data/lib/mongo/collection/view/builder/aggregation.rb +1 -7
  17. data/lib/mongo/collection/view/change_stream.rb +80 -27
  18. data/lib/mongo/collection/view/iterable.rb +76 -60
  19. data/lib/mongo/collection/view/map_reduce.rb +25 -8
  20. data/lib/mongo/collection/view/readable.rb +79 -30
  21. data/lib/mongo/collection/view/writable.rb +109 -48
  22. data/lib/mongo/collection/view.rb +43 -3
  23. data/lib/mongo/collection.rb +158 -23
  24. data/lib/mongo/crypt/auto_encrypter.rb +4 -6
  25. data/lib/mongo/crypt/binding.rb +4 -4
  26. data/lib/mongo/crypt/context.rb +20 -14
  27. data/lib/mongo/crypt/encryption_io.rb +56 -26
  28. data/lib/mongo/crypt/explicit_encrypter.rb +49 -20
  29. data/lib/mongo/crypt/explicit_encryption_context.rb +17 -11
  30. data/lib/mongo/crypt/kms/azure/credentials_retriever.rb +22 -6
  31. data/lib/mongo/crypt/kms/gcp/credentials_retriever.rb +29 -4
  32. data/lib/mongo/csot_timeout_holder.rb +119 -0
  33. data/lib/mongo/cursor/kill_spec.rb +5 -2
  34. data/lib/mongo/cursor/nontailable.rb +27 -0
  35. data/lib/mongo/cursor.rb +86 -24
  36. data/lib/mongo/cursor_host.rb +82 -0
  37. data/lib/mongo/database/view.rb +81 -14
  38. data/lib/mongo/database.rb +88 -18
  39. data/lib/mongo/error/operation_failure.rb +209 -204
  40. data/lib/mongo/error/server_timeout_error.rb +12 -0
  41. data/lib/mongo/error/socket_timeout_error.rb +3 -1
  42. data/lib/mongo/error/timeout_error.rb +23 -0
  43. data/lib/mongo/error.rb +2 -0
  44. data/lib/mongo/grid/fs_bucket.rb +45 -12
  45. data/lib/mongo/grid/stream/read.rb +15 -1
  46. data/lib/mongo/grid/stream/write.rb +21 -4
  47. data/lib/mongo/index/view.rb +77 -16
  48. data/lib/mongo/operation/context.rb +40 -2
  49. data/lib/mongo/operation/create_search_indexes/op_msg.rb +2 -2
  50. data/lib/mongo/operation/delete/op_msg.rb +2 -1
  51. data/lib/mongo/operation/drop_search_index/op_msg.rb +2 -2
  52. data/lib/mongo/operation/find/op_msg.rb +45 -0
  53. data/lib/mongo/operation/get_more/op_msg.rb +33 -0
  54. data/lib/mongo/operation/insert/op_msg.rb +3 -2
  55. data/lib/mongo/operation/insert/result.rb +4 -2
  56. data/lib/mongo/operation/list_collections/result.rb +1 -1
  57. data/lib/mongo/operation/map_reduce/result.rb +1 -1
  58. data/lib/mongo/operation/op_msg_base.rb +3 -1
  59. data/lib/mongo/operation/result.rb +26 -5
  60. data/lib/mongo/operation/shared/executable.rb +12 -1
  61. data/lib/mongo/operation/shared/op_msg_executable.rb +4 -1
  62. data/lib/mongo/operation/shared/response_handling.rb +3 -3
  63. data/lib/mongo/operation/shared/sessions_supported.rb +1 -1
  64. data/lib/mongo/operation/shared/timed.rb +52 -0
  65. data/lib/mongo/operation/shared/write.rb +4 -1
  66. data/lib/mongo/operation/update/op_msg.rb +2 -1
  67. data/lib/mongo/operation/update_search_index/op_msg.rb +2 -2
  68. data/lib/mongo/operation.rb +1 -0
  69. data/lib/mongo/protocol/message.rb +1 -4
  70. data/lib/mongo/protocol/msg.rb +2 -2
  71. data/lib/mongo/retryable/read_worker.rb +69 -29
  72. data/lib/mongo/retryable/write_worker.rb +49 -18
  73. data/lib/mongo/retryable.rb +8 -2
  74. data/lib/mongo/server/connection.rb +11 -5
  75. data/lib/mongo/server/connection_base.rb +22 -2
  76. data/lib/mongo/server/connection_pool.rb +32 -14
  77. data/lib/mongo/server/description/features.rb +1 -1
  78. data/lib/mongo/server/description.rb +18 -5
  79. data/lib/mongo/server/monitor.rb +7 -4
  80. data/lib/mongo/server/pending_connection.rb +7 -3
  81. data/lib/mongo/server/{round_trip_time_averager.rb → round_trip_time_calculator.rb} +25 -7
  82. data/lib/mongo/server.rb +11 -6
  83. data/lib/mongo/server_selector/base.rb +25 -9
  84. data/lib/mongo/session.rb +78 -9
  85. data/lib/mongo/socket/ssl.rb +109 -17
  86. data/lib/mongo/socket/tcp.rb +40 -6
  87. data/lib/mongo/socket.rb +154 -25
  88. data/lib/mongo/uri/options_mapper.rb +1 -0
  89. data/lib/mongo/version.rb +1 -1
  90. data/lib/mongo.rb +1 -0
  91. data/spec/atlas/atlas_connectivity_spec.rb +4 -0
  92. data/spec/atlas/operations_spec.rb +4 -0
  93. data/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb +2 -1
  94. data/spec/integration/client_side_encryption/auto_encryption_spec.rb +494 -487
  95. data/spec/integration/client_side_encryption/on_demand_aws_credentials_spec.rb +1 -1
  96. data/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb +66 -22
  97. data/spec/integration/client_side_operations_timeout/encryption_prose_spec.rb +131 -0
  98. data/spec/integration/connection_pool_populator_spec.rb +2 -0
  99. data/spec/integration/cursor_pinning_spec.rb +15 -60
  100. data/spec/integration/cursor_reaping_spec.rb +1 -1
  101. data/spec/integration/docs_examples_spec.rb +1 -1
  102. data/spec/integration/operation_failure_code_spec.rb +1 -1
  103. data/spec/integration/operation_failure_message_spec.rb +3 -3
  104. data/spec/integration/retryable_errors_spec.rb +2 -2
  105. data/spec/integration/sdam_error_handling_spec.rb +2 -1
  106. data/spec/integration/search_indexes_prose_spec.rb +4 -0
  107. data/spec/integration/server_spec.rb +4 -3
  108. data/spec/integration/transactions_api_examples_spec.rb +2 -0
  109. data/spec/kerberos/kerberos_spec.rb +4 -0
  110. data/spec/lite_spec_helper.rb +3 -1
  111. data/spec/mongo/auth/user/view_spec.rb +1 -1
  112. data/spec/mongo/caching_cursor_spec.rb +1 -1
  113. data/spec/mongo/client_encryption_spec.rb +1 -0
  114. data/spec/mongo/client_spec.rb +158 -4
  115. data/spec/mongo/collection/view/aggregation_spec.rb +14 -39
  116. data/spec/mongo/collection/view/change_stream_spec.rb +3 -3
  117. data/spec/mongo/collection_spec.rb +5 -6
  118. data/spec/mongo/crypt/auto_encrypter_spec.rb +14 -12
  119. data/spec/mongo/crypt/data_key_context_spec.rb +3 -1
  120. data/spec/mongo/crypt/explicit_encryption_context_spec.rb +2 -2
  121. data/spec/mongo/crypt/handle_spec.rb +1 -1
  122. data/spec/mongo/cursor_spec.rb +26 -9
  123. data/spec/mongo/error/operation_failure_heavy_spec.rb +2 -2
  124. data/spec/mongo/operation/context_spec.rb +79 -0
  125. data/spec/mongo/operation/create/op_msg_spec.rb +106 -110
  126. data/spec/mongo/operation/delete/op_msg_spec.rb +6 -5
  127. data/spec/mongo/operation/find/op_msg_spec.rb +66 -0
  128. data/spec/mongo/operation/get_more/op_msg_spec.rb +65 -0
  129. data/spec/mongo/operation/insert/op_msg_spec.rb +128 -131
  130. data/spec/mongo/operation/shared/csot/examples.rb +113 -0
  131. data/spec/mongo/query_cache_spec.rb +243 -225
  132. data/spec/mongo/retryable_spec.rb +1 -0
  133. data/spec/mongo/server/round_trip_time_calculator_spec.rb +120 -0
  134. data/spec/mongo/socket/ssl_spec.rb +0 -10
  135. data/spec/runners/change_streams/test.rb +2 -2
  136. data/spec/runners/crud/operation.rb +1 -1
  137. data/spec/runners/crud/verifier.rb +3 -1
  138. data/spec/runners/transactions/operation.rb +4 -6
  139. data/spec/runners/unified/ambiguous_operations.rb +13 -0
  140. data/spec/runners/unified/assertions.rb +4 -0
  141. data/spec/runners/unified/change_stream_operations.rb +14 -24
  142. data/spec/runners/unified/crud_operations.rb +82 -59
  143. data/spec/runners/unified/ddl_operations.rb +38 -7
  144. data/spec/runners/unified/grid_fs_operations.rb +37 -2
  145. data/spec/runners/unified/support_operations.rb +43 -4
  146. data/spec/runners/unified/test.rb +22 -10
  147. data/spec/runners/unified.rb +1 -1
  148. data/spec/solo/clean_exit_spec.rb +2 -0
  149. data/spec/spec_tests/client_side_operations_timeout_spec.rb +15 -0
  150. data/spec/spec_tests/data/change_streams_unified/change-streams-clusterTime.yml +3 -1
  151. data/spec/spec_tests/data/change_streams_unified/change-streams-disambiguatedPaths.yml +3 -1
  152. data/spec/spec_tests/data/change_streams_unified/change-streams-errors.yml +3 -1
  153. data/spec/spec_tests/data/change_streams_unified/change-streams-pre_and_post_images.yml +1 -1
  154. data/spec/spec_tests/data/change_streams_unified/change-streams-resume-allowlist.yml +1 -1
  155. data/spec/spec_tests/data/change_streams_unified/change-streams-resume-errorLabels.yml +1 -1
  156. data/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml +1 -1
  157. data/spec/spec_tests/data/client_side_encryption/badQueries.yml +2 -1
  158. data/spec/spec_tests/data/client_side_encryption/timeoutMS.yml +67 -0
  159. data/spec/spec_tests/data/client_side_operations_timeout/bulkWrite.yml +87 -0
  160. data/spec/spec_tests/data/client_side_operations_timeout/change-streams.yml +358 -0
  161. data/spec/spec_tests/data/client_side_operations_timeout/close-cursors.yml +129 -0
  162. data/spec/spec_tests/data/client_side_operations_timeout/command-execution.yml +250 -0
  163. data/spec/spec_tests/data/client_side_operations_timeout/convenient-transactions.yml +113 -0
  164. data/spec/spec_tests/data/client_side_operations_timeout/cursors.yml +70 -0
  165. data/spec/spec_tests/data/client_side_operations_timeout/deprecated-options.yml +3982 -0
  166. data/spec/spec_tests/data/client_side_operations_timeout/error-transformations.yml +96 -0
  167. data/spec/spec_tests/data/client_side_operations_timeout/global-timeoutMS.yml +3236 -0
  168. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-advanced.yml +207 -0
  169. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-delete.yml +152 -0
  170. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-download.yml +182 -0
  171. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-find.yml +100 -0
  172. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-upload.yml +249 -0
  173. data/spec/spec_tests/data/client_side_operations_timeout/legacy-timeouts.yml +204 -0
  174. data/spec/spec_tests/data/client_side_operations_timeout/non-tailable-cursors.yml +307 -0
  175. data/spec/spec_tests/data/client_side_operations_timeout/override-collection-timeoutMS.yml +1877 -0
  176. data/spec/spec_tests/data/client_side_operations_timeout/override-operation-timeoutMS.yml +1918 -0
  177. data/spec/spec_tests/data/client_side_operations_timeout/retryability-legacy-timeouts.yml +1676 -0
  178. data/spec/spec_tests/data/client_side_operations_timeout/retryability-timeoutMS.yml +2824 -0
  179. data/spec/spec_tests/data/client_side_operations_timeout/sessions-inherit-timeoutMS.yml +168 -0
  180. data/spec/spec_tests/data/client_side_operations_timeout/sessions-override-operation-timeoutMS.yml +171 -0
  181. data/spec/spec_tests/data/client_side_operations_timeout/sessions-override-timeoutMS.yml +168 -0
  182. data/spec/spec_tests/data/client_side_operations_timeout/tailable-awaitData.yml +247 -0
  183. data/spec/spec_tests/data/client_side_operations_timeout/tailable-non-awaitData.yml +181 -0
  184. data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +4 -0
  185. data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +4 -0
  186. data/spec/spec_tests/data/crud_unified/find-test-all-options.yml +29 -0
  187. data/spec/spec_tests/server_selection_rtt_spec.rb +6 -6
  188. data/spec/support/certificates/atlas-ocsp-ca.crt +81 -83
  189. data/spec/support/certificates/atlas-ocsp.crt +107 -107
  190. data/spec/support/cluster_tools.rb +3 -3
  191. data/spec/support/common_shortcuts.rb +2 -2
  192. data/spec/support/crypt/encrypted_fields/range-encryptedFields-Date.json +1 -1
  193. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalNoPrecision.json +1 -1
  194. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalPrecision.json +1 -1
  195. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DoubleNoPrecision.json +1 -1
  196. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DoublePrecision.json +1 -1
  197. data/spec/support/crypt/encrypted_fields/range-encryptedFields-Int.json +1 -1
  198. data/spec/support/crypt/encrypted_fields/range-encryptedFields-Long.json +1 -1
  199. data/spec/support/shared/session.rb +2 -2
  200. data/spec/support/spec_setup.rb +2 -2
  201. data/spec/support/utils.rb +3 -1
  202. metadata +78 -91
  203. data/spec/mongo/server/round_trip_time_averager_spec.rb +0 -48
  204. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml +0 -242
  205. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml +0 -423
  206. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml +0 -183
  207. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml +0 -240
  208. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml +0 -236
  209. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml +0 -253
  210. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml +0 -1688
  211. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml +0 -294
  212. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml +0 -906
  213. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml +0 -1685
  214. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml +0 -1681
  215. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml +0 -1698
  216. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml +0 -330
  217. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml +0 -425
  218. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml +0 -227
  219. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml +0 -328
  220. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml +0 -320
  221. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml +0 -337
  222. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml +0 -914
  223. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml +0 -293
  224. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml +0 -519
  225. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml +0 -912
  226. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml +0 -908
  227. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml +0 -925
  228. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml +0 -326
  229. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml +0 -425
  230. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml +0 -225
  231. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml +0 -324
  232. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml +0 -320
  233. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml +0 -339
  234. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml +0 -242
  235. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml +0 -424
  236. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml +0 -183
  237. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml +0 -240
  238. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml +0 -236
  239. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml +0 -255
  240. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml +0 -242
  241. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml +0 -423
  242. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml +0 -183
  243. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml +0 -240
  244. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml +0 -236
  245. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml +0 -255
  246. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml +0 -44
@@ -134,7 +134,7 @@ EOT
134
134
  expect(actual).not_to be nil
135
135
  when Hash
136
136
  if actual.is_a?(Hash) && actual['error'] &&
137
- !expected.keys.any? { |key| key.start_with?('error') }
137
+ !expected.keys.any? { |key| key.start_with?('error') || key == 'isTimeoutError' }
138
138
  then
139
139
  raise RSpec::Expectations::ExpectationNotMetError.new,
140
140
  "Expected operation not to fail but it failed: #{actual.inspect}"
@@ -143,6 +143,8 @@ EOT
143
143
 
144
144
  expected.each do |k, v|
145
145
  case k
146
+ when 'isTimeoutError'
147
+ expect(actual['errorContains']).to eq('Mongo::Error::TimeoutError')
146
148
  when 'errorContains'
147
149
  expect(actual['errorContains'].downcase).to include(v.downcase)
148
150
  when 'errorLabelsContain'
@@ -43,12 +43,10 @@ module Mongo
43
43
  end
44
44
 
45
45
  result
46
- rescue Mongo::Error::OperationFailure => e
47
- result = e.instance_variable_get(:@result)
48
- if result.nil?
49
- raise "OperationFailure had nil result: #{e}"
50
- end
51
- err_doc = result.send(:first_document)
46
+ rescue Mongo::Error::OperationFailure::Family => e
47
+ raise "OperationFailure had nil result: #{e}" if e.result.nil?
48
+
49
+ err_doc = e.result.send(:first_document)
52
50
  error_code_name = err_doc['codeName'] || err_doc['writeConcernError'] && err_doc['writeConcernError']['codeName']
53
51
  if error_code_name.nil?
54
52
  # Sometimes the server does not return the error code name,
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unified
4
+ module AmbiguousOperations
5
+ def find(op)
6
+ entities.get(:collection, op['object'])
7
+ crud_find(op)
8
+ rescue Unified::Error::EntityMissing
9
+ entities.get(:bucket, op['object'])
10
+ gridfs_find(op)
11
+ end
12
+ end
13
+ end
@@ -379,6 +379,10 @@ module Unified
379
379
  unless actual == result
380
380
  raise Error::ResultMismatch, "Actual value #{actual} does not match entity #{expected_v} with value #{result}"
381
381
  end
382
+ when '$$lte'
383
+ if actual.nil? || actual >= expected_v
384
+ raise Error::ResultMismatch, "Actual value #{actual} should be less than #{expected_v}"
385
+ end
382
386
  else
383
387
  raise NotImplementedError, "Unknown operator #{operator}"
384
388
  end
@@ -10,22 +10,9 @@ module Unified
10
10
  object = entities.get_any(object_id)
11
11
  use_arguments(op) do |args|
12
12
  pipeline = args.use!('pipeline')
13
- opts = {}
14
- if batch_size = args.use('batchSize')
15
- opts[:batch_size] = batch_size
16
- end
17
- if comment = args.use('comment')
18
- opts[:comment] = comment
19
- end
20
- if full_document = args.use('fullDocument')
21
- opts[:full_document] = full_document
22
- end
23
- if full_document_before_change = args.use('fullDocumentBeforeChange')
24
- opts[:full_document_before_change] = full_document_before_change
25
- end
26
- if args.key?('showExpandedEvents')
27
- opts[:show_expanded_events] = args.use!('showExpandedEvents')
28
- end
13
+ opts = extract_options(args, 'batchSize', 'comment', 'fullDocument',
14
+ 'fullDocumentBeforeChange', 'showExpandedEvents', 'timeoutMS',
15
+ 'maxAwaitTimeMS')
29
16
  cs = object.watch(pipeline, **opts)
30
17
  if name = op.use('saveResultAsEntity')
31
18
  entities.set(:change_stream, name, cs)
@@ -35,18 +22,21 @@ module Unified
35
22
 
36
23
  def iterate_until_document_or_error(op)
37
24
  object_id = op.use!('object')
38
- object = entities.get(:change_stream, object_id)
39
- object.to_enum.next
25
+ object = entities.get_any(object_id)
26
+ object.try_next
27
+ end
28
+
29
+ def iterate_once(op)
30
+ stream_id = op.use!('object')
31
+ stream = entities.get_any(stream_id)
32
+ stream.try_next
40
33
  end
41
34
 
42
35
  def close(op)
43
36
  object_id = op.use!('object')
44
- # The Ruby driver unified spec runner does not currently implement
45
- # find cursors as created by createFindCursor. This will be done
46
- # as part of CSOT implementation. When this is done, the line(s) below
47
- # should be changed to retrieve such cursor instances and close them.
48
- object = entities.get(:csot_cursor, object_id)
49
- object.close
37
+ opts = op.key?('arguments') ? extract_options(op.use!('arguments'), 'timeoutMS') : {}
38
+ object = entities.get_any(object_id)
39
+ object.close(opts)
50
40
  end
51
41
  end
52
42
  end
@@ -5,7 +5,7 @@ module Unified
5
5
 
6
6
  module CrudOperations
7
7
 
8
- def find(op)
8
+ def crud_find(op)
9
9
  get_find_view(op).to_a
10
10
  end
11
11
 
@@ -16,35 +16,21 @@ module Unified
16
16
  def get_find_view(op)
17
17
  collection = entities.get(:collection, op.use!('object'))
18
18
  use_arguments(op) do |args|
19
- opts = {
20
- let: args.use('let'),
21
- comment: args.use('comment'),
22
- allow_disk_use: args.use('allowDiskUse'),
23
- show_disk_loc: args.use('showRecordId'),
24
- return_key: args.use('returnKey'),
25
- projection: args.use('projection'),
26
- skip: args.use('skip'),
27
- hint: args.use('hint'),
28
- max_value: args.use('max'),
29
- max_time_ms: args.use('maxTimeMS'),
30
- min_value: args.use('min'),
31
- }
32
- if session = args.use('session')
33
- opts[:session] = entities.get(:session, session)
34
- end
35
- if collation = args.use('collation')
36
- opts[:collation] = collation
37
- end
38
- if args.key?('noCursorTimeout')
39
- opts[:no_cursor_timeout] = args.use('noCursorTimeout')
40
- end
41
- if args.key?('oplogReplay')
42
- opts[:oplog_replay] = args.use('oplogReplay')
43
- end
44
- if args.key?('allowPartialResults')
45
- opts[:allow_partial_results] = args.use('allowPartialResults')
46
- end
47
- req = collection.find(args.use!('filter'), **opts)
19
+ filter = args.use!('filter')
20
+ session = args.use('session')
21
+
22
+ opts = extract_options(args, 'let', 'comment',
23
+ 'allowDiskUse', 'returnKey', 'projection',
24
+ 'skip', 'hint', 'maxTimeMS', 'timeoutMS',
25
+ 'collation', 'noCursorTimeout', 'oplogReplay', 'allowPartialResults',
26
+ 'timeoutMode', 'maxAwaitTimeMS', 'cursorType', 'timeoutMode',
27
+ { 'showRecordId' => :show_disk_loc, 'max' => :max_value, 'min' => :min_value },
28
+ allow_extra: true)
29
+ symbolize_options!(opts, :timeout_mode, :cursor_type)
30
+
31
+ opts[:session] = entities.get(:session, session) if session
32
+
33
+ req = collection.find(filter, **opts)
48
34
  if batch_size = args.use('batchSize')
49
35
  req = req.batch_size(batch_size)
50
36
  end
@@ -61,15 +47,23 @@ module Unified
61
47
  end
62
48
  end
63
49
 
64
- def count_documents(op)
50
+ def count(op)
65
51
  collection = entities.get(:collection, op.use!('object'))
66
52
  use_arguments(op) do |args|
67
- opts = {}
53
+ opts = extract_options(args, 'comment', 'timeoutMS', 'maxTimeMS', allow_extra: true)
68
54
  if session = args.use('session')
69
55
  opts[:session] = entities.get(:session, session)
70
56
  end
71
- if comment = args.use('comment')
72
- opts[:comment] = comment
57
+ collection.count(args.use!('filter'), **opts)
58
+ end
59
+ end
60
+
61
+ def count_documents(op)
62
+ collection = entities.get(:collection, op.use!('object'))
63
+ use_arguments(op) do |args|
64
+ opts = extract_options(args, 'comment', 'timeoutMS', 'maxTimeMS', allow_extra: true)
65
+ if session = args.use('session')
66
+ opts[:session] = entities.get(:session, session)
73
67
  end
74
68
  collection.find(args.use!('filter')).count_documents(**opts)
75
69
  end
@@ -78,12 +72,9 @@ module Unified
78
72
  def estimated_document_count(op)
79
73
  collection = entities.get(:collection, op.use!('object'))
80
74
  use_arguments(op) do |args|
81
- opts = {}
82
- if max_time_ms = args.use('maxTimeMS')
83
- opts[:max_time_ms] = max_time_ms
84
- end
85
- if comment = args.use('comment')
86
- opts[:comment] = comment
75
+ opts = extract_options(args, 'comment', 'timeoutMS', 'maxTimeMS', allow_extra: true)
76
+ if session = args.use('session')
77
+ opts[:session] = entities.get(:session, session)
87
78
  end
88
79
  collection.estimated_document_count(**opts)
89
80
  end
@@ -92,13 +83,10 @@ module Unified
92
83
  def distinct(op)
93
84
  collection = entities.get(:collection, op.use!('object'))
94
85
  use_arguments(op) do |args|
95
- opts = {}
86
+ opts = extract_options(args, 'comment', 'timeoutMS', 'maxTimeMS', allow_extra: true)
96
87
  if session = args.use('session')
97
88
  opts[:session] = entities.get(:session, session)
98
89
  end
99
- if comment = args.use('comment')
100
- opts[:comment] = comment
101
- end
102
90
  req = collection.find(args.use!('filter'), **opts).distinct(args.use!('fieldName'), **opts)
103
91
  result = req.to_a
104
92
  end
@@ -114,6 +102,8 @@ module Unified
114
102
  comment: args.use('comment'),
115
103
  hint: args.use('hint'),
116
104
  upsert: args.use('upsert'),
105
+ timeout_ms: args.use('timeoutMS'),
106
+ max_time_ms: args.use('maxTimeMS')
117
107
  }
118
108
  if return_document = args.use('returnDocument')
119
109
  opts[:return_document] = return_document.downcase.to_sym
@@ -134,6 +124,8 @@ module Unified
134
124
  let: args.use('let'),
135
125
  comment: args.use('comment'),
136
126
  hint: args.use('hint'),
127
+ timeout_ms: args.use('timeoutMS'),
128
+ max_time_ms: args.use('maxTimeMS')
137
129
  }
138
130
  if session = args.use('session')
139
131
  opts[:session] = entities.get(:session, session)
@@ -150,6 +142,8 @@ module Unified
150
142
  let: args.use('let'),
151
143
  comment: args.use('comment'),
152
144
  hint: args.use('hint'),
145
+ timeout_ms: args.use('timeoutMS'),
146
+ max_time_ms: args.use('maxTimeMS')
153
147
  }
154
148
  if session = args.use('session')
155
149
  opts[:session] = entities.get(:session, session)
@@ -162,7 +156,9 @@ module Unified
162
156
  collection = entities.get(:collection, op.use!('object'))
163
157
  use_arguments(op) do |args|
164
158
  opts = {
165
- comment: args.use('comment')
159
+ comment: args.use('comment'),
160
+ timeout_ms: args.use('timeoutMS'),
161
+ max_time_ms: args.use('maxTimeMS')
166
162
  }
167
163
  if session = args.use('session')
168
164
  opts[:session] = entities.get(:session, session)
@@ -175,7 +171,9 @@ module Unified
175
171
  collection = entities.get(:collection, op.use!('object'))
176
172
  use_arguments(op) do |args|
177
173
  opts = {
178
- comment: args.use('comment')
174
+ comment: args.use('comment'),
175
+ timeout_ms: args.use('timeoutMS'),
176
+ max_time_ms: args.use('maxTimeMS')
179
177
  }
180
178
  unless (ordered = args.use('ordered')).nil?
181
179
  opts[:ordered] = ordered
@@ -195,6 +193,8 @@ module Unified
195
193
  comment: args.use('comment'),
196
194
  hint: args.use('hint'),
197
195
  upsert: args.use('upsert'),
196
+ timeout_ms: args.use('timeoutMS'),
197
+ max_time_ms: args.use('maxTimeMS')
198
198
  }
199
199
  if session = args.use('session')
200
200
  opts[:session] = entities.get(:session, session)
@@ -210,6 +210,8 @@ module Unified
210
210
  let: args.use('let'),
211
211
  comment: args.use('comment'),
212
212
  hint: args.use('hint'),
213
+ timeout_ms: args.use('timeoutMS'),
214
+ max_time_ms: args.use('maxTimeMS')
213
215
  }
214
216
  collection.update_many(args.use!('filter'), args.use!('update'), **opts)
215
217
  end
@@ -224,7 +226,9 @@ module Unified
224
226
  comment: args.use('comment'),
225
227
  upsert: args.use('upsert'),
226
228
  let: args.use('let'),
227
- hint: args.use('hint')
229
+ hint: args.use('hint'),
230
+ timeout_ms: args.use('timeoutMS'),
231
+ max_time_ms: args.use('maxTimeMS')
228
232
  )
229
233
  end
230
234
  end
@@ -236,6 +240,8 @@ module Unified
236
240
  let: args.use('let'),
237
241
  comment: args.use('comment'),
238
242
  hint: args.use('hint'),
243
+ timeout_ms: args.use('timeoutMS'),
244
+ max_time_ms: args.use('maxTimeMS')
239
245
  }
240
246
  if session = args.use('session')
241
247
  opts[:session] = entities.get(:session, session)
@@ -251,6 +257,8 @@ module Unified
251
257
  let: args.use('let'),
252
258
  comment: args.use('comment'),
253
259
  hint: args.use('hint'),
260
+ timeout_ms: args.use('timeoutMS'),
261
+ max_time_ms: args.use('maxTimeMS')
254
262
  }
255
263
  collection.delete_many(args.use!('filter'), **opts)
256
264
  end
@@ -272,6 +280,12 @@ module Unified
272
280
  if let = args.use('let')
273
281
  opts[:let] = let
274
282
  end
283
+ if timeout_ms = args.use('timeoutMS')
284
+ opts[:timeout_ms] = timeout_ms
285
+ end
286
+ if max_time_ms = args.use('maxTimeMS')
287
+ opts[:max_time_ms] = max_time_ms
288
+ end
275
289
  collection.bulk_write(requests, **opts)
276
290
  end
277
291
  end
@@ -280,27 +294,36 @@ module Unified
280
294
  obj = entities.get_any(op.use!('object'))
281
295
  args = op.use!('arguments')
282
296
  pipeline = args.use!('pipeline')
283
- opts = {
284
- let: args.use('let'),
285
- }
297
+
298
+ opts = extract_options(args, 'let', 'comment', 'batchSize', 'maxTimeMS',
299
+ 'allowDiskUse', 'timeoutMode', 'timeoutMS', 'maxTimeMS', allow_extra: true)
300
+ symbolize_options!(opts, :timeout_mode)
301
+
286
302
  if session = args.use('session')
287
303
  opts[:session] = entities.get(:session, session)
288
304
  end
289
- if comment = args.use('comment')
290
- opts[:comment] = comment
291
- end
292
- if batch_size = args.use('batchSize')
293
- opts[:batch_size] = batch_size
294
- end
295
- if args.key?('allowDiskUse')
296
- opts[:allow_disk_use] = args.use('allowDiskUse')
297
- end
305
+
298
306
  unless args.empty?
299
307
  raise NotImplementedError, "Unhandled spec keys: #{args} in #{test_spec}"
300
308
  end
309
+
301
310
  obj.aggregate(pipeline, **opts).to_a
302
311
  end
303
312
 
313
+ def create_find_cursor(op)
314
+ obj = entities.get_any(op.use!('object'))
315
+ args = op.use!('arguments')
316
+
317
+ filter = args.use('filter')
318
+ opts = extract_options(args, 'batchSize', 'timeoutMS', 'cursorType', 'maxAwaitTimeMS')
319
+ symbolize_options!(opts, :cursor_type)
320
+
321
+ view = obj.find(filter, opts)
322
+ view.each # to initialize the cursor
323
+
324
+ view.cursor
325
+ end
326
+
304
327
  private
305
328
 
306
329
  def convert_bulk_write_spec(spec)
@@ -20,6 +20,9 @@ module Unified
20
20
  if session = args.use('session')
21
21
  opts[:session] = entities.get(:session, session)
22
22
  end
23
+ if timeout_ms = args.use('timeoutMS')
24
+ opts[:timeout_ms] = timeout_ms
25
+ end
23
26
  client.list_databases(args.use('filter') || {}, name_only, **opts)
24
27
  end
25
28
  end
@@ -50,6 +53,15 @@ module Unified
50
53
  if pipeline = args.use('pipeline')
51
54
  collection_opts[:pipeline] = pipeline
52
55
  end
56
+ if capped = args.use('capped')
57
+ collection_opts[:capped] = capped
58
+ end
59
+ if size = args.use('size')
60
+ collection_opts[:size] = size
61
+ end
62
+ if max = args.use('max')
63
+ collection_opts[:max] = max
64
+ end
53
65
  database[args.use!('collection'), collection_opts].create(**opts)
54
66
  end
55
67
  end
@@ -65,13 +77,16 @@ module Unified
65
77
  def list_colls(op, name_only: false)
66
78
  database = entities.get(:database, op.use!('object'))
67
79
  use_arguments(op) do |args|
68
- opts = {}
80
+ opts = extract_options(args, 'filter', 'timeoutMode', allow_extra: true)
81
+ symbolize_options!(opts, :timeout_mode)
82
+
69
83
  if session = args.use('session')
70
84
  opts[:session] = entities.get(:session, session)
71
85
  end
72
- if filter = args.use('filter')
73
- opts[:filter] = filter
86
+ if timeout_ms = args.use('timeoutMS')
87
+ opts[:timeout_ms] = timeout_ms
74
88
  end
89
+
75
90
  database.list_collections(**opts.merge(name_only: name_only))
76
91
  end
77
92
  end
@@ -126,14 +141,25 @@ module Unified
126
141
  def list_indexes(op)
127
142
  collection = entities.get(:collection, op.use!('object'))
128
143
  use_arguments(op) do |args|
129
- opts = {}
144
+ opts = extract_options(args, 'timeoutMode', allow_extra: true)
130
145
  if session = args.use('session')
131
146
  opts[:session] = entities.get(:session, session)
132
147
  end
148
+ if timeout_ms = args.use('timeoutMS')
149
+ opts[:timeout_ms] = timeout_ms
150
+ end
133
151
  collection.indexes(**opts).to_a
134
152
  end
135
153
  end
136
154
 
155
+ def drop_indexes(op)
156
+ collection = entities.get(:collection, op.use!('object'))
157
+ use_arguments(op) do |args|
158
+ opts = extract_options(args, 'maxTimeMS', 'timeoutMS', allow_extra: true)
159
+ collection.indexes.drop_all(**opts)
160
+ end
161
+ end
162
+
137
163
  def create_index(op)
138
164
  collection = entities.get(:collection, op.use!('object'))
139
165
  use_arguments(op) do |args|
@@ -144,7 +170,12 @@ module Unified
144
170
  if args.key?('unique')
145
171
  opts[:unique] = args.use('unique')
146
172
  end
147
-
173
+ if timeout_ms = args.use('timeoutMS')
174
+ opts[:timeout_ms] = timeout_ms
175
+ end
176
+ if max_time_ms = args.use('maxTimeMS')
177
+ opts[:max_time_ms] = max_time_ms
178
+ end
148
179
  collection.indexes.create_one(
149
180
  args.use!('keys'),
150
181
  name: args.use('name'),
@@ -156,7 +187,7 @@ module Unified
156
187
  def drop_index(op)
157
188
  collection = entities.get(:collection, op.use!('object'))
158
189
  use_arguments(op) do |args|
159
- opts = {}
190
+ opts = extract_options(args, 'maxTimeMS', 'timeoutMS', allow_extra: true)
160
191
  if session = args.use('session')
161
192
  opts[:session] = entities.get(:session, session)
162
193
  end
@@ -188,7 +219,7 @@ module Unified
188
219
  begin
189
220
  index = collection.indexes.get(args.use!('indexName'))
190
221
  raise Error::ResultMismatch, "Index found"
191
- rescue Mongo::Error::OperationFailure => e
222
+ rescue Mongo::Error::OperationFailure::Family => e
192
223
  if e.code == 26
193
224
  # OK
194
225
  else
@@ -5,17 +5,38 @@ module Unified
5
5
 
6
6
  module GridFsOperations
7
7
 
8
+ def gridfs_find(op)
9
+ bucket = entities.get(:bucket, op.use!('object'))
10
+ use_arguments(op) do |args|
11
+ filter = args.use!('filter')
12
+
13
+ opts = extract_options(args, 'allowDiskUse',
14
+ 'skip', 'hint','timeoutMS',
15
+ 'noCursorTimeout', 'sort', 'limit')
16
+
17
+ bucket.find(filter,opts).to_a
18
+ end
19
+ end
20
+
8
21
  def delete(op)
9
22
  bucket = entities.get(:bucket, op.use!('object'))
10
23
  use_arguments(op) do |args|
11
- bucket.delete(args.use!('id'))
24
+ opts = {}
25
+ if timeout_ms = args.use('timeoutMS')
26
+ opts[:timeout_ms] = timeout_ms
27
+ end
28
+ bucket.delete(args.use!('id'), opts)
12
29
  end
13
30
  end
14
31
 
15
32
  def download(op)
16
33
  bucket = entities.get(:bucket, op.use!('object'))
17
34
  use_arguments(op) do |args|
18
- stream = bucket.open_download_stream(args.use!('id'))
35
+ opts = {}
36
+ if timeout_ms = args.use('timeoutMS')
37
+ opts[:timeout_ms] = timeout_ms
38
+ end
39
+ stream = bucket.open_download_stream(args.use!('id'), opts)
19
40
  stream.read
20
41
  end
21
42
  end
@@ -48,6 +69,9 @@ module Unified
48
69
  if disable_md5 = args.use('disableMD5')
49
70
  opts[:disable_md5] = disable_md5
50
71
  end
72
+ if timeout_ms = args.use('timeoutMS')
73
+ opts[:timeout_ms] = timeout_ms
74
+ end
51
75
  contents = transform_contents(args.use!('source'))
52
76
  file_id = nil
53
77
  bucket.open_upload_stream(args.use!('filename'), **opts) do |stream|
@@ -58,6 +82,17 @@ module Unified
58
82
  end
59
83
  end
60
84
 
85
+ def drop(op)
86
+ bucket = entities.get(:bucket, op.use!('object'))
87
+ use_arguments(op) do |args|
88
+ opts = {}
89
+ if timeout_ms = args.use('timeoutMS')
90
+ opts[:timeout_ms] = timeout_ms
91
+ end
92
+ bucket.drop(opts)
93
+ end
94
+ end
95
+
61
96
  private
62
97
 
63
98
  def transform_contents(contents)
@@ -20,6 +20,9 @@ module Unified
20
20
  if read_preference = args.use('readPreference')
21
21
  opts[:read] = ::Utils.snakeize_hash(read_preference)
22
22
  end
23
+ if timeout_ms = args.use('timeoutMS')
24
+ opts[:timeout_ms] = timeout_ms
25
+ end
23
26
 
24
27
  database.command(cmd, **opts)
25
28
  end
@@ -129,14 +132,20 @@ module Unified
129
132
 
130
133
  def commit_transaction(op)
131
134
  session = entities.get(:session, op.use!('object'))
132
- assert_no_arguments(op)
133
- session.commit_transaction
135
+ opts = {}
136
+ use_arguments(op) do |args|
137
+ opts[:timeout_ms] = args.use('timeoutMS')
138
+ end
139
+ session.commit_transaction(opts.compact)
134
140
  end
135
141
 
136
142
  def abort_transaction(op)
137
143
  session = entities.get(:session, op.use!('object'))
138
- assert_no_arguments(op)
139
- session.abort_transaction
144
+ opts = {}
145
+ use_arguments(op) do |args|
146
+ opts[:timeout_ms] = args.use('timeoutMS')
147
+ end
148
+ session.abort_transaction(opts.compact)
140
149
  end
141
150
 
142
151
  def with_transaction(op)
@@ -311,6 +320,36 @@ module Unified
311
320
 
312
321
  private
313
322
 
323
+ # @param [ UsingHash ] args the arguments to extract options from
324
+ # @param [ Array<String | Hash> ] keys an array of strings and Hashes,
325
+ # where Hashes represent a mapping from the MDB key to the correspoding
326
+ # Ruby key. For Strings, the Ruby key is assumed to be a simple conversion
327
+ # of the MDB key, from camel-case to snake-case.
328
+ # @param [ true | false ] allow_extra whether or not extra keys are allowed
329
+ # to exist in the args hash, beyond those listed.
330
+ def extract_options(args, *keys, allow_extra: false)
331
+ {}.tap do |opts|
332
+ keys.each do |key|
333
+ Array(key).each do |mdb_key, ruby_key|
334
+ value = args.use(mdb_key)
335
+ opts[ruby_key || mdb_name_to_ruby(mdb_key)] = value unless value.nil?
336
+ end
337
+ end
338
+
339
+ raise NotImplementedError, "unhandled keys: #{args}" if !allow_extra && !args.empty?
340
+ end
341
+ end
342
+
343
+ def symbolize_options!(opts, *keys)
344
+ keys.each do |key|
345
+ opts[key] = mdb_name_to_ruby(opts[key]) if opts[key]
346
+ end
347
+ end
348
+
349
+ def mdb_name_to_ruby(name)
350
+ name.to_s.gsub(/([a-z])([A-Z])/) { "#{$1}_#{$2}" }.downcase.to_sym
351
+ end
352
+
314
353
  def assert_no_arguments(op)
315
354
  if op.key?('arguments')
316
355
  raise NotimplementedError, "Arguments are not allowed"