mongo 2.5.3 → 2.6.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 (394) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE +1 -1
  5. data/README.md +3 -2
  6. data/lib/mongo.rb +2 -2
  7. data/lib/mongo/address.rb +10 -2
  8. data/lib/mongo/address/ipv4.rb +1 -1
  9. data/lib/mongo/address/ipv6.rb +26 -5
  10. data/lib/mongo/address/unix.rb +1 -1
  11. data/lib/mongo/auth.rb +10 -3
  12. data/lib/mongo/auth/cr.rb +4 -1
  13. data/lib/mongo/auth/cr/conversation.rb +4 -1
  14. data/lib/mongo/auth/ldap.rb +1 -1
  15. data/lib/mongo/auth/ldap/conversation.rb +1 -1
  16. data/lib/mongo/auth/roles.rb +1 -1
  17. data/lib/mongo/auth/scram.rb +24 -7
  18. data/lib/mongo/auth/scram/conversation.rb +52 -19
  19. data/lib/mongo/auth/stringprep.rb +114 -0
  20. data/lib/mongo/auth/stringprep/profiles/sasl.rb +73 -0
  21. data/lib/mongo/auth/stringprep/tables.rb +3232 -0
  22. data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +174 -0
  23. data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +1170 -0
  24. data/lib/mongo/auth/user.rb +14 -3
  25. data/lib/mongo/auth/user/view.rb +1 -1
  26. data/lib/mongo/auth/x509.rb +1 -1
  27. data/lib/mongo/auth/x509/conversation.rb +1 -1
  28. data/lib/mongo/bson.rb +1 -1
  29. data/lib/mongo/bulk_write.rb +8 -8
  30. data/lib/mongo/bulk_write/combineable.rb +1 -1
  31. data/lib/mongo/bulk_write/ordered_combiner.rb +1 -1
  32. data/lib/mongo/bulk_write/result.rb +1 -1
  33. data/lib/mongo/bulk_write/result_combiner.rb +4 -4
  34. data/lib/mongo/bulk_write/transformable.rb +1 -1
  35. data/lib/mongo/bulk_write/unordered_combiner.rb +1 -1
  36. data/lib/mongo/bulk_write/validatable.rb +1 -1
  37. data/lib/mongo/client.rb +115 -24
  38. data/lib/mongo/cluster.rb +17 -10
  39. data/lib/mongo/cluster/app_metadata.rb +7 -1
  40. data/lib/mongo/cluster/periodic_executor.rb +1 -1
  41. data/lib/mongo/cluster/reapers/socket_reaper.rb +1 -1
  42. data/lib/mongo/cluster/topology.rb +12 -2
  43. data/lib/mongo/cluster/topology/replica_set.rb +9 -1
  44. data/lib/mongo/cluster/topology/sharded.rb +1 -1
  45. data/lib/mongo/cluster/topology/single.rb +1 -1
  46. data/lib/mongo/cluster/topology/unknown.rb +1 -1
  47. data/lib/mongo/collection.rb +75 -19
  48. data/lib/mongo/collection/view.rb +1 -1
  49. data/lib/mongo/collection/view/aggregation.rb +1 -1
  50. data/lib/mongo/collection/view/builder.rb +1 -1
  51. data/lib/mongo/collection/view/builder/aggregation.rb +3 -3
  52. data/lib/mongo/collection/view/builder/find_command.rb +1 -1
  53. data/lib/mongo/collection/view/builder/flags.rb +1 -1
  54. data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
  55. data/lib/mongo/collection/view/builder/modifiers.rb +1 -1
  56. data/lib/mongo/collection/view/builder/op_query.rb +1 -1
  57. data/lib/mongo/collection/view/change_stream.rb +193 -17
  58. data/lib/mongo/collection/view/change_stream/retryable.rb +3 -20
  59. data/lib/mongo/collection/view/explainable.rb +1 -1
  60. data/lib/mongo/collection/view/immutable.rb +1 -1
  61. data/lib/mongo/collection/view/iterable.rb +2 -2
  62. data/lib/mongo/collection/view/map_reduce.rb +1 -1
  63. data/lib/mongo/collection/view/readable.rb +108 -29
  64. data/lib/mongo/collection/view/writable.rb +3 -3
  65. data/lib/mongo/cursor.rb +44 -4
  66. data/lib/mongo/cursor/builder.rb +1 -1
  67. data/lib/mongo/cursor/builder/get_more_command.rb +1 -1
  68. data/lib/mongo/cursor/builder/kill_cursors_command.rb +1 -1
  69. data/lib/mongo/cursor/builder/op_get_more.rb +1 -1
  70. data/lib/mongo/cursor/builder/op_kill_cursors.rb +1 -1
  71. data/lib/mongo/database.rb +46 -3
  72. data/lib/mongo/database/view.rb +11 -11
  73. data/lib/mongo/dbref.rb +1 -1
  74. data/lib/mongo/error.rb +57 -1
  75. data/lib/mongo/error/bulk_write_error.rb +2 -2
  76. data/lib/mongo/error/change_stream_resumable.rb +37 -0
  77. data/lib/mongo/error/closed_stream.rb +1 -1
  78. data/lib/mongo/error/extra_file_chunk.rb +1 -1
  79. data/lib/mongo/error/failed_stringprep_validation.rb +38 -0
  80. data/lib/mongo/error/file_not_found.rb +1 -1
  81. data/lib/mongo/error/insufficient_iteration_count.rb +38 -0
  82. data/lib/mongo/error/invalid_application_name.rb +1 -1
  83. data/lib/mongo/error/invalid_bulk_operation.rb +1 -1
  84. data/lib/mongo/error/invalid_bulk_operation_type.rb +1 -1
  85. data/lib/mongo/error/invalid_collection_name.rb +1 -1
  86. data/lib/mongo/error/invalid_database_name.rb +1 -1
  87. data/lib/mongo/error/invalid_document.rb +1 -1
  88. data/lib/mongo/error/invalid_file.rb +1 -1
  89. data/lib/mongo/error/invalid_file_revision.rb +1 -1
  90. data/lib/mongo/error/invalid_min_pool_size.rb +1 -1
  91. data/lib/mongo/error/invalid_nonce.rb +1 -1
  92. data/lib/mongo/error/invalid_read_option.rb +35 -0
  93. data/lib/mongo/error/invalid_replacement_document.rb +1 -1
  94. data/lib/mongo/error/invalid_server_preference.rb +1 -1
  95. data/lib/mongo/error/invalid_session.rb +1 -1
  96. data/lib/mongo/error/invalid_signature.rb +1 -1
  97. data/lib/mongo/error/invalid_transaction_operation.rb +82 -0
  98. data/lib/mongo/error/invalid_txt_record.rb +1 -1
  99. data/lib/mongo/error/invalid_update_document.rb +1 -1
  100. data/lib/mongo/error/invalid_uri.rb +1 -1
  101. data/lib/mongo/error/invalid_write_concern.rb +1 -1
  102. data/lib/mongo/error/max_bson_size.rb +1 -1
  103. data/lib/mongo/error/max_message_size.rb +1 -1
  104. data/lib/mongo/error/mismatched_domain.rb +1 -1
  105. data/lib/mongo/error/missing_file_chunk.rb +1 -1
  106. data/lib/mongo/error/missing_resume_token.rb +1 -1
  107. data/lib/mongo/error/multi_index_drop.rb +1 -1
  108. data/lib/mongo/error/need_primary_server.rb +1 -1
  109. data/lib/mongo/error/no_server_available.rb +1 -1
  110. data/lib/mongo/error/no_srv_records.rb +1 -1
  111. data/lib/mongo/error/operation_failure.rb +108 -14
  112. data/lib/mongo/error/parser.rb +50 -1
  113. data/lib/mongo/error/socket_error.rb +5 -2
  114. data/lib/mongo/error/socket_timeout_error.rb +5 -2
  115. data/lib/mongo/error/unchangeable_collection_option.rb +1 -1
  116. data/lib/mongo/error/unexpected_chunk_length.rb +1 -1
  117. data/lib/mongo/error/unexpected_response.rb +1 -1
  118. data/lib/mongo/error/unknown_payload_type.rb +1 -1
  119. data/lib/mongo/error/unsupported_array_filters.rb +1 -1
  120. data/lib/mongo/error/unsupported_collation.rb +1 -1
  121. data/lib/mongo/error/unsupported_features.rb +1 -1
  122. data/lib/mongo/error/unsupported_message_type.rb +1 -1
  123. data/lib/mongo/error/write_retryable.rb +27 -0
  124. data/lib/mongo/event.rb +10 -9
  125. data/lib/mongo/event/base.rb +33 -0
  126. data/lib/mongo/event/description_changed.rb +2 -2
  127. data/lib/mongo/event/listeners.rb +1 -1
  128. data/lib/mongo/event/member_discovered.rb +4 -2
  129. data/lib/mongo/event/primary_elected.rb +2 -2
  130. data/lib/mongo/event/publisher.rb +1 -1
  131. data/lib/mongo/event/standalone_discovered.rb +2 -2
  132. data/lib/mongo/event/subscriber.rb +1 -1
  133. data/lib/mongo/grid.rb +1 -1
  134. data/lib/mongo/grid/file.rb +1 -1
  135. data/lib/mongo/grid/file/chunk.rb +3 -3
  136. data/lib/mongo/grid/file/info.rb +26 -3
  137. data/lib/mongo/grid/fs_bucket.rb +1 -1
  138. data/lib/mongo/grid/stream.rb +1 -1
  139. data/lib/mongo/grid/stream/read.rb +1 -1
  140. data/lib/mongo/grid/stream/write.rb +1 -1
  141. data/lib/mongo/index.rb +1 -1
  142. data/lib/mongo/index/view.rb +1 -1
  143. data/lib/mongo/loggable.rb +1 -1
  144. data/lib/mongo/logger.rb +1 -1
  145. data/lib/mongo/monitoring.rb +99 -62
  146. data/lib/mongo/monitoring/command_log_subscriber.rb +2 -2
  147. data/lib/mongo/monitoring/event.rb +2 -1
  148. data/lib/mongo/monitoring/event/command_failed.rb +19 -6
  149. data/lib/mongo/monitoring/event/command_started.rb +14 -3
  150. data/lib/mongo/monitoring/event/command_succeeded.rb +5 -3
  151. data/lib/mongo/monitoring/event/secure.rb +1 -1
  152. data/lib/mongo/monitoring/event/server_closed.rb +2 -2
  153. data/lib/mongo/monitoring/event/server_description_changed.rb +2 -2
  154. data/lib/mongo/monitoring/event/server_opening.rb +11 -2
  155. data/lib/mongo/monitoring/event/topology_changed.rb +13 -2
  156. data/lib/mongo/monitoring/event/topology_closed.rb +2 -2
  157. data/lib/mongo/monitoring/event/topology_opening.rb +11 -2
  158. data/lib/mongo/monitoring/publishable.rb +10 -6
  159. data/lib/mongo/monitoring/sdam_log_subscriber.rb +1 -1
  160. data/lib/mongo/monitoring/server_closed_log_subscriber.rb +1 -1
  161. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +1 -1
  162. data/lib/mongo/monitoring/server_opening_log_subscriber.rb +1 -1
  163. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
  164. data/lib/mongo/monitoring/topology_opening_log_subscriber.rb +1 -1
  165. data/lib/mongo/operation/aggregate/op_msg.rb +3 -0
  166. data/lib/mongo/operation/create/op_msg.rb +9 -0
  167. data/lib/mongo/operation/create_index/op_msg.rb +9 -0
  168. data/lib/mongo/operation/create_user/command.rb +1 -1
  169. data/lib/mongo/operation/create_user/op_msg.rb +10 -1
  170. data/lib/mongo/operation/delete/op_msg.rb +3 -0
  171. data/lib/mongo/operation/distinct/op_msg.rb +9 -0
  172. data/lib/mongo/operation/drop/op_msg.rb +9 -0
  173. data/lib/mongo/operation/drop_database/op_msg.rb +9 -0
  174. data/lib/mongo/operation/drop_index/op_msg.rb +9 -0
  175. data/lib/mongo/operation/explain/op_msg.rb +3 -0
  176. data/lib/mongo/operation/find/op_msg.rb +3 -0
  177. data/lib/mongo/operation/get_more.rb +1 -1
  178. data/lib/mongo/operation/get_more/command.rb +1 -1
  179. data/lib/mongo/operation/get_more/legacy.rb +1 -1
  180. data/lib/mongo/operation/get_more/op_msg.rb +3 -0
  181. data/lib/mongo/operation/indexes/op_msg.rb +3 -0
  182. data/lib/mongo/operation/indexes/result.rb +1 -1
  183. data/lib/mongo/operation/insert/bulk_result.rb +32 -2
  184. data/lib/mongo/operation/insert/op_msg.rb +3 -0
  185. data/lib/mongo/operation/insert/result.rb +1 -1
  186. data/lib/mongo/operation/kill_cursors/op_msg.rb +9 -0
  187. data/lib/mongo/operation/list_collections/op_msg.rb +3 -0
  188. data/lib/mongo/operation/list_collections/result.rb +5 -1
  189. data/lib/mongo/operation/map_reduce/op_msg.rb +3 -0
  190. data/lib/mongo/operation/map_reduce/result.rb +1 -1
  191. data/lib/mongo/operation/parallel_scan/op_msg.rb +3 -0
  192. data/lib/mongo/operation/remove_user/op_msg.rb +9 -0
  193. data/lib/mongo/operation/result.rb +27 -14
  194. data/lib/mongo/operation/shared/executable.rb +1 -0
  195. data/lib/mongo/operation/shared/sessions_supported.rb +78 -7
  196. data/lib/mongo/operation/shared/specifiable.rb +18 -2
  197. data/lib/mongo/operation/shared/write_concern_supported.rb +1 -1
  198. data/lib/mongo/operation/update/op_msg.rb +3 -0
  199. data/lib/mongo/operation/update_user/command.rb +1 -1
  200. data/lib/mongo/operation/update_user/op_msg.rb +10 -1
  201. data/lib/mongo/operation/users_info/op_msg.rb +3 -0
  202. data/lib/mongo/options.rb +1 -1
  203. data/lib/mongo/options/mapper.rb +1 -1
  204. data/lib/mongo/options/redacted.rb +1 -1
  205. data/lib/mongo/protocol/bit_vector.rb +1 -1
  206. data/lib/mongo/protocol/compressed.rb +1 -1
  207. data/lib/mongo/protocol/delete.rb +1 -1
  208. data/lib/mongo/protocol/get_more.rb +7 -7
  209. data/lib/mongo/protocol/insert.rb +1 -1
  210. data/lib/mongo/protocol/kill_cursors.rb +1 -1
  211. data/lib/mongo/protocol/message.rb +5 -5
  212. data/lib/mongo/protocol/msg.rb +9 -7
  213. data/lib/mongo/protocol/query.rb +1 -1
  214. data/lib/mongo/protocol/registry.rb +1 -1
  215. data/lib/mongo/protocol/reply.rb +10 -10
  216. data/lib/mongo/protocol/serializers.rb +1 -1
  217. data/lib/mongo/protocol/update.rb +1 -1
  218. data/lib/mongo/retryable.rb +22 -14
  219. data/lib/mongo/server.rb +1 -1
  220. data/lib/mongo/server/connectable.rb +1 -1
  221. data/lib/mongo/server/connection.rb +16 -4
  222. data/lib/mongo/server/connection_pool.rb +1 -1
  223. data/lib/mongo/server/connection_pool/queue.rb +1 -1
  224. data/lib/mongo/server/context.rb +1 -1
  225. data/lib/mongo/server/description.rb +14 -2
  226. data/lib/mongo/server/description/features.rb +10 -9
  227. data/lib/mongo/server/description/inspector.rb +1 -1
  228. data/lib/mongo/server/description/inspector/description_changed.rb +1 -1
  229. data/lib/mongo/server/description/inspector/member_discovered.rb +1 -1
  230. data/lib/mongo/server/description/inspector/primary_elected.rb +1 -1
  231. data/lib/mongo/server/description/inspector/standalone_discovered.rb +1 -1
  232. data/lib/mongo/server/monitor.rb +15 -3
  233. data/lib/mongo/server/monitor/connection.rb +1 -1
  234. data/lib/mongo/server_selector.rb +1 -1
  235. data/lib/mongo/server_selector/nearest.rb +1 -1
  236. data/lib/mongo/server_selector/primary.rb +1 -1
  237. data/lib/mongo/server_selector/primary_preferred.rb +1 -1
  238. data/lib/mongo/server_selector/secondary.rb +1 -1
  239. data/lib/mongo/server_selector/secondary_preferred.rb +1 -1
  240. data/lib/mongo/server_selector/selectable.rb +7 -2
  241. data/lib/mongo/session.rb +389 -12
  242. data/lib/mongo/session/server_session.rb +7 -2
  243. data/lib/mongo/session/session_pool.rb +1 -1
  244. data/lib/mongo/socket.rb +1 -1
  245. data/lib/mongo/socket/ssl.rb +1 -1
  246. data/lib/mongo/socket/tcp.rb +1 -1
  247. data/lib/mongo/socket/unix.rb +1 -1
  248. data/lib/mongo/uri.rb +6 -4
  249. data/lib/mongo/uri/srv_protocol.rb +1 -1
  250. data/lib/mongo/version.rb +2 -2
  251. data/lib/mongo/write_concern.rb +1 -1
  252. data/lib/mongo/write_concern/acknowledged.rb +1 -1
  253. data/lib/mongo/write_concern/normalizable.rb +1 -1
  254. data/lib/mongo/write_concern/unacknowledged.rb +1 -1
  255. data/mongo.gemspec +4 -1
  256. data/spec/atlas/atlas_connectivity_spec.rb +54 -0
  257. data/spec/integration/bulk_insert_spec.rb +78 -0
  258. data/spec/integration/change_stream_spec.rb +365 -0
  259. data/spec/integration/command_monitoring_spec.rb +92 -0
  260. data/spec/lite_spec_helper.rb +63 -0
  261. data/spec/mongo/address/ipv6_spec.rb +29 -1
  262. data/spec/mongo/address_spec.rb +34 -0
  263. data/spec/mongo/auth/scram/conversation_spec.rb +326 -120
  264. data/spec/mongo/auth/scram/negotiation_spec.rb +574 -0
  265. data/spec/mongo/auth/scram_spec.rb +107 -38
  266. data/spec/mongo/auth/stringprep/profiles/sasl_spec.rb +113 -0
  267. data/spec/mongo/auth/stringprep_spec.rb +188 -0
  268. data/spec/mongo/auth/user/view_spec.rb +5 -2
  269. data/spec/mongo/auth/user_spec.rb +1 -1
  270. data/spec/mongo/bulk_write/result_spec.rb +120 -0
  271. data/spec/mongo/bulk_write_spec.rb +42 -2
  272. data/spec/mongo/client_spec.rb +121 -9
  273. data/spec/mongo/cluster/app_metadata_spec.rb +14 -1
  274. data/spec/mongo/cluster/topology_spec.rb +1 -23
  275. data/spec/mongo/collection/view/change_stream_spec.rb +62 -180
  276. data/spec/mongo/collection_spec.rb +45 -12
  277. data/spec/mongo/cursor/builder/get_more_command_spec.rb +7 -7
  278. data/spec/mongo/cursor_spec.rb +2 -2
  279. data/spec/mongo/database_spec.rb +3 -3
  280. data/spec/mongo/docs_examples_spec.rb +194 -0
  281. data/spec/mongo/error/operation_failure_spec.rb +152 -0
  282. data/spec/mongo/error/parser_spec.rb +127 -0
  283. data/spec/mongo/grid/fs_bucket_spec.rb +32 -0
  284. data/spec/mongo/grid/stream/write_spec.rb +40 -1
  285. data/spec/mongo/monitoring/event/command_failed_spec.rb +30 -0
  286. data/spec/mongo/monitoring/event/command_started_spec.rb +26 -4
  287. data/spec/mongo/monitoring/event/command_succeeded_spec.rb +29 -7
  288. data/spec/mongo/monitoring_spec.rb +28 -3
  289. data/spec/mongo/protocol/get_more_spec.rb +2 -2
  290. data/spec/mongo/retryable_spec.rb +252 -34
  291. data/spec/mongo/retryable_writes_spec.rb +468 -544
  292. data/spec/mongo/server/connection_spec.rb +5 -5
  293. data/spec/mongo/server/description_spec.rb +23 -6
  294. data/spec/mongo/session/server_session_spec.rb +2 -2
  295. data/spec/mongo/session/session_pool_spec.rb +2 -2
  296. data/spec/mongo/transactions_examples_spec.rb +227 -0
  297. data/spec/mongo/transactions_spec.rb +44 -0
  298. data/spec/spec_helper.rb +135 -49
  299. data/spec/spec_tests/change_streams_spec.rb +42 -0
  300. data/spec/{mongo → spec_tests}/command_monitoring_spec.rb +8 -2
  301. data/spec/{mongo → spec_tests}/connection_string_spec.rb +1 -1
  302. data/spec/{mongo → spec_tests}/crud_spec.rb +5 -7
  303. data/spec/{mongo → spec_tests}/dns_seedlist_discovery_spec.rb +1 -1
  304. data/spec/{mongo → spec_tests}/gridfs_spec.rb +0 -0
  305. data/spec/{mongo → spec_tests}/max_staleness_spec.rb +0 -0
  306. data/spec/spec_tests/retryable_writes_spec.rb +78 -0
  307. data/spec/{mongo → spec_tests}/sdam_monitoring_spec.rb +4 -3
  308. data/spec/{mongo → spec_tests}/sdam_spec.rb +7 -7
  309. data/spec/{mongo → spec_tests}/server_selection_rtt_spec.rb +0 -0
  310. data/spec/{mongo → spec_tests}/server_selection_spec.rb +0 -0
  311. data/spec/support/authorization.rb +18 -6
  312. data/spec/support/change_streams.rb +265 -0
  313. data/spec/support/change_streams/operation.rb +62 -0
  314. data/spec/support/change_streams_tests/change-streams-errors.yml +53 -0
  315. data/spec/support/change_streams_tests/change-streams.yml +299 -0
  316. data/spec/support/command_monitoring.rb +1 -1
  317. data/spec/support/command_monitoring/bulkWrite.yml +4 -28
  318. data/spec/support/command_monitoring/command.yml +19 -0
  319. data/spec/support/command_monitoring/find.yml +17 -19
  320. data/spec/support/command_monitoring/insertMany.yml +2 -8
  321. data/spec/support/command_monitoring/unacknowledgedBulkWrite.yml +34 -0
  322. data/spec/support/connection_string.rb +1 -1
  323. data/spec/support/constraints.rb +56 -0
  324. data/spec/support/crud.rb +9 -4
  325. data/spec/support/crud/read.rb +24 -3
  326. data/spec/support/crud/write.rb +7 -2
  327. data/spec/support/crud_tests/read/count-collation.yml +12 -2
  328. data/spec/support/crud_tests/read/count.yml +43 -5
  329. data/spec/support/gridfs.rb +1 -1
  330. data/spec/support/primary_socket.rb +21 -0
  331. data/spec/support/retryable_writes_tests/bulkWrite-serverErrors.yml +90 -0
  332. data/spec/support/retryable_writes_tests/bulkWrite.yml +99 -1
  333. data/spec/support/retryable_writes_tests/deleteOne-serverErrors.yml +50 -0
  334. data/spec/support/retryable_writes_tests/deleteOne.yml +10 -1
  335. data/spec/support/retryable_writes_tests/findOneAndDelete-serverErrors.yml +50 -0
  336. data/spec/support/retryable_writes_tests/findOneAndDelete.yml +39 -30
  337. data/spec/support/retryable_writes_tests/findOneAndReplace-serverErrors.yml +54 -0
  338. data/spec/support/retryable_writes_tests/findOneAndReplace.yml +9 -0
  339. data/spec/support/retryable_writes_tests/findOneAndUpdate-serverErrors.yml +54 -0
  340. data/spec/support/retryable_writes_tests/findOneAndUpdate.yml +9 -0
  341. data/spec/support/retryable_writes_tests/insertMany-serverErrors.yml +59 -0
  342. data/spec/support/retryable_writes_tests/insertMany.yml +11 -6
  343. data/spec/support/retryable_writes_tests/insertOne-serverErrors.yml +471 -0
  344. data/spec/support/retryable_writes_tests/insertOne.yml +9 -0
  345. data/spec/support/retryable_writes_tests/replaceOne-serverErrors.yml +58 -0
  346. data/spec/support/retryable_writes_tests/replaceOne.yml +9 -0
  347. data/spec/support/retryable_writes_tests/updateOne-serverErrors.yml +58 -0
  348. data/spec/support/retryable_writes_tests/updateOne.yml +71 -53
  349. data/spec/support/sdam/rs/normalize_case_me.yml +100 -0
  350. data/spec/support/sdam/sharded/compatible.yml +38 -0
  351. data/spec/support/sdam/sharded/mongos_disconnect.yml +9 -3
  352. data/spec/support/sdam/sharded/multiple_mongoses.yml +6 -2
  353. data/spec/support/sdam/sharded/non_mongos_removed.yml +6 -2
  354. data/spec/support/sdam/sharded/too_new.yml +36 -0
  355. data/spec/support/sdam/sharded/too_old.yml +36 -0
  356. data/spec/support/sdam/single/compatible.yml +26 -0
  357. data/spec/support/sdam/single/direct_connection_external_ip.yml +3 -1
  358. data/spec/support/sdam/single/direct_connection_mongos.yml +3 -1
  359. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +3 -1
  360. data/spec/support/sdam/single/direct_connection_rsprimary.yml +3 -1
  361. data/spec/support/sdam/single/direct_connection_rssecondary.yml +3 -1
  362. data/spec/support/sdam/single/direct_connection_slave.yml +3 -1
  363. data/spec/support/sdam/single/direct_connection_standalone.yml +3 -1
  364. data/spec/support/sdam/single/not_ok_response.yml +6 -2
  365. data/spec/support/sdam/single/standalone_removed.yml +3 -1
  366. data/spec/support/sdam/single/too_new.yml +26 -0
  367. data/spec/support/sdam/single/too_old.yml +24 -0
  368. data/spec/support/shared/session.rb +107 -0
  369. data/spec/support/transactions.rb +391 -0
  370. data/spec/support/transactions/operation.rb +373 -0
  371. data/spec/support/transactions_tests/abort.yml +403 -0
  372. data/spec/support/transactions_tests/bulk.yml +267 -0
  373. data/spec/support/transactions_tests/causal-consistency.yml +173 -0
  374. data/spec/support/transactions_tests/commit.yml +593 -0
  375. data/spec/support/transactions_tests/delete.yml +184 -0
  376. data/spec/support/transactions_tests/error-labels.yml +948 -0
  377. data/spec/support/transactions_tests/errors.yml +125 -0
  378. data/spec/support/transactions_tests/findOneAndDelete.yml +126 -0
  379. data/spec/support/transactions_tests/findOneAndReplace.yml +140 -0
  380. data/spec/support/transactions_tests/findOneAndUpdate.yml +228 -0
  381. data/spec/support/transactions_tests/insert.yml +264 -0
  382. data/spec/support/transactions_tests/isolation.yml +125 -0
  383. data/spec/support/transactions_tests/read-pref.yml +340 -0
  384. data/spec/support/transactions_tests/reads.yml +298 -0
  385. data/spec/support/transactions_tests/retryable-abort.yml +1292 -0
  386. data/spec/support/transactions_tests/retryable-commit.yml +1332 -0
  387. data/spec/support/transactions_tests/retryable-writes.yml +208 -0
  388. data/spec/support/transactions_tests/run-command.yml +189 -0
  389. data/spec/support/transactions_tests/transaction-options.yml +877 -0
  390. data/spec/support/transactions_tests/update.yml +246 -0
  391. data/spec/support/transactions_tests/write-concern.yml +236 -0
  392. metadata +494 -359
  393. metadata.gz.sig +0 -0
  394. data/lib/csasl/csasl.bundle +0 -0
@@ -0,0 +1,391 @@
1
+ # Copyright (C) 2014-2018 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Matcher for determining if the results of the operation match the
16
+ # test's expected results.
17
+ #
18
+ # @since 2.6.0
19
+
20
+ # Matcher for determining if the collection's data matches the
21
+ # test's expected collection data.
22
+ #
23
+ # @since 2.6.0
24
+ RSpec::Matchers.define :match_collection_data do |test|
25
+
26
+ match do
27
+ test.compare_collection_data
28
+ end
29
+ end
30
+
31
+ RSpec::Matchers.define :match_operation_result do |test|
32
+
33
+ match do |actual|
34
+ test.compare_operation_result(actual)
35
+ end
36
+ end
37
+
38
+ require 'support/transactions/operation'
39
+
40
+ module Mongo
41
+ module Transactions
42
+
43
+ # Represents a Transactions specification test.
44
+ #
45
+ # @since 2.6.0
46
+ class Spec
47
+
48
+ # The name of the collection to run the tests against.
49
+ #
50
+ # @since 2.6.0
51
+ COLLECTION_NAME = 'transactions-tests'.freeze
52
+
53
+ # @return [ String ] description The spec description.
54
+ #
55
+ # @since 2.6.0
56
+ attr_reader :description
57
+
58
+ # Instantiate the new spec.
59
+ #
60
+ # @example Create the spec.
61
+ # Spec.new(file)
62
+ #
63
+ # @param [ String ] file The name of the file.
64
+ #
65
+ # @since 2.6.0
66
+ def initialize(file)
67
+ file = File.new(file)
68
+ @spec = YAML.load(ERB.new(file.read).result)
69
+ file.close
70
+ @description = File.basename(file)
71
+ @data = @spec['data']
72
+ @transaction_tests = @spec['tests']
73
+ end
74
+
75
+ # Get a list of TransactionTests for each test definition.
76
+ #
77
+ # @example Get the list of TransactionTests.
78
+ # spec.tests
79
+ #
80
+ # @return [ Array<TransactionsTest> ] The list of TransactionTests.
81
+ #
82
+ # @since 2.6.0
83
+ def tests
84
+ @transaction_tests.collect do |test|
85
+ Mongo::Transactions::TransactionsTest.new(@data, test)
86
+ end
87
+ end
88
+ end
89
+
90
+ # Represents a single transaction test.
91
+ #
92
+ # @since 2.6.0
93
+ class TransactionsTest
94
+
95
+ # The test description.
96
+ #
97
+ # @return [ String ] description The test description.
98
+ #
99
+ # @since 2.6.0
100
+ attr_reader :description
101
+
102
+ # The expected command monitoring events
103
+ #
104
+ # @since 2.6.0
105
+ attr_reader :expectations
106
+
107
+ # Instantiate the new CRUDTest.
108
+ #
109
+ # @example Create the test.
110
+ # TransactionTest.new(data, test)
111
+ #
112
+ # @param [ Array<Hash> ] data The documents the collection
113
+ # must have before the test runs.
114
+ # @param [ Hash ] test The test specification.
115
+ #
116
+ # @since 2.6.0
117
+ def initialize(data, test)
118
+ @data = data
119
+ @description = test['description']
120
+ @client_options = convert_client_options(test['clientOptions'] || {})
121
+ @session_options = snakeize_hash(test['sessionOptions'] || {})
122
+ @failpoint = test['failPoint']
123
+ @operations = test['operations']
124
+ @expectations = test['expectations']
125
+ @outcome = test['outcome']
126
+ @expected_results = @operations.map do |o|
127
+ result = o['result']
128
+ next result unless result.class == Hash
129
+
130
+ # Change maps of result ids to arrays of ids
131
+ result.tap do |r|
132
+ r.each do |k, v|
133
+ next unless ['insertedIds', 'upsertedIds'].include?(k)
134
+ r[k] = v.to_a.sort_by(&:first).map(&:last)
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ # Run the test.
141
+ #
142
+ # @example Run the test.
143
+ # test.run
144
+ #
145
+ # @return [ Result ] The result of running the test.
146
+ #
147
+ # @since 2.6.0
148
+ def run
149
+ @collection.client.subscribe(Mongo::Monitoring::COMMAND, EventSubscriber.clear_events!)
150
+
151
+ results = @ops.map { |o| o.execute(@collection, @session0, @session1) }
152
+
153
+ session0_id = @session0.session_id
154
+ session1_id = @session1.session_id
155
+
156
+ @session0.end_session
157
+ @session1.end_session
158
+
159
+ events = EventSubscriber.started_events.map do |e|
160
+
161
+ # Convert txnNumber field from a BSON integer to an extended JSON int64
162
+ if e.command['txnNumber']
163
+ e.command['txnNumber'] = {
164
+ '$numberLong' => e.command['txnNumber'].instance_variable_get(:@integer).to_s
165
+ }
166
+ end
167
+
168
+ # Replace the session id placeholders with the actual session ids.
169
+ e.command['lsid'] = 'session0' if e.command['lsid'] == session0_id
170
+ e.command['lsid'] = 'session1' if e.command['lsid'] == session1_id
171
+
172
+
173
+ # The spec files don't include these fields, so we delete them.
174
+ e.command.delete('$readPreference')
175
+ e.command.delete('bypassDocumentValidation')
176
+ e.command.delete('$clusterTime')
177
+
178
+
179
+ if e.command['readConcern']
180
+ # The spec test use an afterClusterTime value of 42 to indicate that we need to assert
181
+ # that the field exists in the actual read concern rather than comparing the value, so
182
+ # we replace any afterClusterTime value with 42.
183
+ if e.command['readConcern']['afterClusterTime']
184
+ e.command['readConcern']['afterClusterTime'] = 42
185
+ end
186
+
187
+ # Convert the readConcern level from a symbol to a string.
188
+ if e.command['readConcern']['level']
189
+ e.command['readConcern']['level'] = e.command['readConcern']['level'].to_s
190
+ end
191
+ end
192
+
193
+ # This write concern is sent for some server topologies/configurations, but not all, so it
194
+ # doesn't appear in the expected events.
195
+ e.command.delete('writeConcern') if e.command['writeConcern'] == { 'w' => 2 }
196
+
197
+ # The spec tests use 42 as a placeholder value for any getMore cursorId.
198
+ e.command['getMore'] = { '$numberLong' => '42' } if e.command['getMore']
199
+
200
+ # Remove fields if empty
201
+ e.command.delete('cursor') if e.command['cursor'] && e.command['cursor'].empty?
202
+ e.command.delete('filter') if e.command['filter'] && e.command['filter'].empty?
203
+ e.command.delete('query') if e.command['query'] && e.command['query'].empty?
204
+
205
+ {
206
+ 'command_started_event' => {
207
+ 'command' => e.command.to_a.sort,
208
+ 'command_name' => e.command_name.to_s,
209
+ 'database_name' => e.database_name
210
+ }
211
+ }
212
+ end
213
+
214
+ # Remove any events from authentication commands.
215
+ events.reject! { |c| c['command_started_event']['command_name'].start_with?('sasl') }
216
+
217
+ if @failpoint
218
+ ADMIN_AUTHORIZED_TEST_CLIENT.use('admin').command(configureFailPoint: 'failCommand', mode: 'off')
219
+ end
220
+
221
+ {
222
+ results: results,
223
+ contents: @collection.find.to_a,
224
+ events: events
225
+ }
226
+ end
227
+
228
+ def setup_test
229
+ client = ADMIN_AUTHORIZED_TEST_CLIENT.use('admin')
230
+
231
+ begin
232
+ client.command(killAllSessions: [])
233
+ rescue Mongo::Error
234
+ end
235
+
236
+ db = client.use(AUTHORIZED_CLIENT.database.name)
237
+ coll = db[Mongo::Transactions::Spec::COLLECTION_NAME]
238
+ coll.with(write: { w: :majority }).drop
239
+ db.command(
240
+ { create: Mongo::Transactions::Spec::COLLECTION_NAME },
241
+ { write_concern: { w: :majority } })
242
+
243
+ coll.with(write: { w: :majority }).insert_many(@data) unless @data.empty?
244
+ client.command(@failpoint) if @failpoint
245
+
246
+ client.close
247
+
248
+ test_client = AUTHORIZED_CLIENT.with(
249
+ @client_options.merge(app_name: 'this is used solely to force the new client to create its own cluster'))
250
+ @collection = test_client[Mongo::Transactions::Spec::COLLECTION_NAME]
251
+
252
+ @session0 = test_client.start_session(@session_options[:session0] || {})
253
+ @session1 = test_client.start_session(@session_options[:session1] || {})
254
+
255
+ @ops = @operations.reduce([]) do |ops, op|
256
+ arguments = case op['arguments'] && op['arguments']['session']
257
+ when 'session0'
258
+ op['arguments'].merge('session' => @session0)
259
+ when 'session1'
260
+ op['arguments'].merge('session' => @session1)
261
+ else
262
+ op['arguments']
263
+ end
264
+
265
+ ops << Operation.new(op.merge('arguments' => arguments))
266
+ end
267
+ end
268
+
269
+ def teardown_test
270
+ @collection.database.drop
271
+ @collection.client.close
272
+ end
273
+
274
+ # Compare the existing collection data and the expected collection data.
275
+ #
276
+ # @example Compare the existing and expected collection data.
277
+ # test.compare_collection_data
278
+ #
279
+ # @return [ true, false ] The result of comparing the existing and expected
280
+ # collection data.
281
+ #
282
+ # @since 2.6.0
283
+ def compare_collection_data
284
+ if actual_collection_data.nil?
285
+ outcome_collection_data.nil?
286
+ elsif actual_collection_data.empty?
287
+ outcome_collection_data.empty?
288
+ else
289
+ actual_collection_data.all? do |doc|
290
+ outcome_collection_data.include?(doc)
291
+ end
292
+ end
293
+ end
294
+
295
+ # Compare the actual operation result to the expected operation result.
296
+ #
297
+ # @example Compare the existing and expected operation results.
298
+ # test.compare_operation_result(actual_results)
299
+ #
300
+ # @params [ Object ] actual The actual test results.
301
+ #
302
+ # @return [ true, false ] The result of comparing the expected and actual operation result.
303
+ #
304
+ # @since 2.6.0
305
+ def compare_operation_result(actual_results)
306
+ return false if @expected_results.length != actual_results.length
307
+
308
+ @expected_results.zip(actual_results).all? do |expected, actual|
309
+ compare_result(expected, actual)
310
+ end
311
+ end
312
+
313
+ # The expected data in the collection as an outcome after running this test.
314
+ #
315
+ # @example Get the outcome collection data
316
+ # test.outcome_collection_data
317
+ #
318
+ # @return [ Array<Hash> ] The list of documents expected to be in the collection
319
+ # after running this test.
320
+ #
321
+ # @since 2.6.0
322
+ def outcome_collection_data
323
+ @outcome['collection']['data'] if @outcome && @outcome['collection']
324
+ end
325
+
326
+ private
327
+
328
+ def convert_client_options(client_options)
329
+ client_options.reduce({}) do |opts, kv|
330
+ case kv.first
331
+ when 'readConcernLevel'
332
+ kv = [:read_concern, { 'level' => kv.last }]
333
+ when 'readPreference'
334
+ kv = [:read, { 'mode' => kv.last }]
335
+ when 'w'
336
+ kv = [:write, { w: kv.last }]
337
+ else
338
+ kv[0] = camel_to_snake(kv[0])
339
+ end
340
+
341
+ opts.tap { |o| o[kv.first] = kv.last }
342
+ end
343
+ end
344
+
345
+ def compare_result(expected, actual)
346
+ case expected
347
+ when nil
348
+ actual.nil?
349
+ when Hash
350
+ expected.all? do |k, v|
351
+ case k
352
+ when 'errorContains'
353
+ actual && actual['errorContains'].include?(v)
354
+ when 'errorLabelsContain'
355
+ actual && v.all? { |label| actual['errorLabels'].include?(label) }
356
+ when 'errorLabelsOmit'
357
+ !actual || v.all? { |label| !actual['errorLabels'].include?(label) }
358
+ else
359
+ actual && (actual[k] == v || handle_upserted_id(k, v, actual[v]) ||
360
+ handle_inserted_ids(k, v, actual[v]))
361
+ end
362
+ end
363
+ else
364
+ expected == actual
365
+ end
366
+ end
367
+
368
+ def handle_upserted_id(field, expected_id, actual_id)
369
+ return true if expected_id.nil?
370
+ if field == 'upsertedId'
371
+ if expected_id.is_a?(Integer)
372
+ actual_id.is_a?(BSON::ObjectId) || actual_id.nil?
373
+ end
374
+ end
375
+ end
376
+
377
+ def handle_inserted_ids(field, expected, actual)
378
+ if field == 'insertedIds'
379
+ expected.values == actual
380
+ end
381
+ end
382
+
383
+ def actual_collection_data
384
+ if @outcome['collection']
385
+ collection_name = @outcome['collection']['name'] || @collection.name
386
+ @collection.database[collection_name].find.to_a
387
+ end
388
+ end
389
+ end
390
+ end
391
+ end
@@ -0,0 +1,373 @@
1
+ # Copyright (C) 2014-2018 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module Transactions
17
+ class Operation
18
+
19
+ # Map of operation names to method names.
20
+ #
21
+ # @since 2.6.0
22
+ OPERATIONS = {
23
+ 'startTransaction' => :start_transaction,
24
+ 'abortTransaction' => :abort_transaction,
25
+ 'commitTransaction' => :commit_transaction,
26
+ 'aggregate' => :aggregate,
27
+ 'deleteMany' => :delete_many,
28
+ 'deleteOne' => :delete_one,
29
+ 'insertMany' => :insert_many,
30
+ 'insertOne' => :insert_one,
31
+ 'replaceOne' => :replace_one,
32
+ 'updateMany' => :update_many,
33
+ 'updateOne' => :update_one,
34
+ 'findOneAndDelete' => :find_one_and_delete,
35
+ 'findOneAndReplace' => :find_one_and_replace,
36
+ 'findOneAndUpdate' => :find_one_and_update,
37
+ 'bulkWrite' => :bulk_write,
38
+ 'count' => :count,
39
+ 'distinct' => :distinct,
40
+ 'find' => :find,
41
+ 'runCommand' => :run_command
42
+ }.freeze
43
+
44
+ # Map of operation options to method names.
45
+ #
46
+ # @since 2.6.0
47
+ ARGUMENT_MAP = {
48
+ array_filters: 'arrayFilters',
49
+ batch_size: 'batchSize',
50
+ collation: 'collation',
51
+ read_preference: 'readPreference',
52
+ document: 'document',
53
+ field_name: 'fieldName',
54
+ filter: 'filter',
55
+ ordered: 'ordered',
56
+ pipeline: 'pipeline',
57
+ projection: 'projection',
58
+ replacement: 'replacement',
59
+ return_document: 'returnDocument',
60
+ session: 'session',
61
+ sort: 'sort',
62
+ update: 'update',
63
+ upsert: 'upsert'
64
+ }.freeze
65
+
66
+ # The operation name.
67
+ #
68
+ # @return [ String ] name The operation name.
69
+ #
70
+ # @since 2.6.0
71
+ attr_reader :name
72
+
73
+ # Instantiate the operation.
74
+ #
75
+ # @return [ Hash ] spec The operation spec.
76
+ #
77
+ # @since 2.6.0
78
+ def initialize(spec)
79
+ @spec = spec
80
+ @name = spec['name']
81
+ end
82
+
83
+ # Execute the operation.
84
+ #
85
+ # @example Execute the operation.
86
+ # operation.execute
87
+ #
88
+ # @param [ Collection ] collection The collection to execute
89
+ # the operation on.
90
+ #
91
+ # @return [ Result ] The result of executing the operation.
92
+ #
93
+ # @since 2.6.0
94
+ def execute(collection, session0, session1)
95
+ # Determine which object the operation method should be called on.
96
+ obj = case object
97
+ when 'session0'
98
+ session0
99
+ when 'session1'
100
+ session1
101
+ when 'database'
102
+ collection.database
103
+ else
104
+ collection = collection.with(read: read_preference) if collection_read_preference
105
+ collection = collection.with(read_concern: read_concern) if read_concern
106
+ collection = collection.with(write: write_concern) if write_concern
107
+ collection
108
+ end
109
+
110
+ send(OPERATIONS[name], obj)
111
+ rescue Mongo::Error::OperationFailure => e
112
+ err_doc = e.instance_variable_get(:@result).send(:first_document)
113
+
114
+ {
115
+ 'errorCodeName' => err_doc['codeName'] || err_doc['writeConcernError']['codeName'],
116
+ 'errorContains' => e.message,
117
+ 'errorLabels' => (e.instance_variable_get(:@labels) || []) + (err_doc['errorLabels'] || [])
118
+ }
119
+ rescue Mongo::Error => e
120
+ {
121
+ 'errorContains' => e.message,
122
+ 'errorLabels' => e.instance_variable_get(:@labels) || []
123
+ }
124
+ end
125
+
126
+ private
127
+
128
+ def start_transaction(session)
129
+ session.start_transaction(snakeize_hash(arguments['options'])) ; nil
130
+ end
131
+
132
+ def commit_transaction(session)
133
+ session.commit_transaction ; nil
134
+ end
135
+
136
+ def abort_transaction(session)
137
+ session.abort_transaction ; nil
138
+ end
139
+
140
+ def run_command(database)
141
+ # Convert the first key (i.e. the command name) to a symbol.
142
+ cmd = command.dup
143
+ command_name = cmd.first.first
144
+ command_value = cmd.delete(command_name)
145
+ cmd = { command_name.to_sym => command_value }.merge(cmd)
146
+
147
+ opts = snakeize_hash(options)
148
+ opts[:read] = opts.delete(:read_preference)
149
+ database.command(cmd, opts).documents.first
150
+ end
151
+
152
+ def aggregate(collection)
153
+ collection.aggregate(pipeline, options).to_a
154
+ end
155
+
156
+ def bulk_write(collection)
157
+ result = collection.bulk_write(requests, options)
158
+ return_doc = {}
159
+ return_doc['deletedCount'] = result.deleted_count || 0
160
+ return_doc['insertedIds'] = result.inserted_ids if result.inserted_ids
161
+ return_doc['upsertedId'] = result.upserted_id if upsert
162
+ return_doc['upsertedCount'] = result.upserted_count || 0
163
+ return_doc['matchedCount'] = result.matched_count || 0
164
+ return_doc['modifiedCount'] = result.modified_count || 0
165
+ return_doc['upsertedIds'] = result.upserted_ids if result.upserted_ids
166
+ return_doc
167
+ end
168
+
169
+ def count(collection)
170
+ collection.count(filter, options).to_s
171
+ end
172
+
173
+ def delete_many(collection)
174
+ result = collection.delete_many(filter, options)
175
+ { 'deletedCount' => result.deleted_count }
176
+ end
177
+
178
+ def delete_one(collection)
179
+ result = collection.delete_one(filter, options)
180
+ { 'deletedCount' => result.deleted_count }
181
+ end
182
+
183
+ def distinct(collection)
184
+ collection.distinct(field_name, filter, options)
185
+ end
186
+
187
+ def find(collection)
188
+ opts = modifiers ? options.merge(modifiers: BSON::Document.new(modifiers)) : options
189
+ collection.find(filter, opts).to_a
190
+ end
191
+
192
+ def insert_many(collection)
193
+ result = collection.insert_many(documents, options)
194
+ { 'insertedIds' => result.inserted_ids }
195
+ end
196
+
197
+ def insert_one(collection)
198
+ result = collection.insert_one(document, options)
199
+ { 'insertedId' => result.inserted_id }
200
+ end
201
+
202
+ def update_return_doc(result)
203
+ return_doc = {}
204
+ return_doc['upsertedId'] = result.upserted_id if upsert
205
+ return_doc['upsertedCount'] = result.upserted_count
206
+ return_doc['matchedCount'] = result.matched_count
207
+ return_doc['modifiedCount'] = result.modified_count if result.modified_count
208
+ return_doc
209
+ end
210
+
211
+ def replace_one(collection)
212
+ result = collection.replace_one(filter, replacement, options)
213
+ update_return_doc(result)
214
+ end
215
+
216
+ def update_many(collection)
217
+ result = collection.update_many(filter, update, options)
218
+ update_return_doc(result)
219
+ end
220
+
221
+ def update_one(collection)
222
+ result = collection.update_one(filter, update, options)
223
+ update_return_doc(result)
224
+ end
225
+
226
+ def find_one_and_delete(collection)
227
+ collection.find_one_and_delete(filter, options)
228
+ end
229
+
230
+ def find_one_and_replace(collection)
231
+ collection.find_one_and_replace(filter, replacement, options)
232
+ end
233
+
234
+ def find_one_and_update(collection)
235
+ collection.find_one_and_update(filter, update, options)
236
+ end
237
+
238
+ def object
239
+ @spec['object']
240
+ end
241
+
242
+ def options
243
+ ARGUMENT_MAP.reduce({}) do |opts, (key, value)|
244
+ arguments.key?(value) ? opts.merge!(key => send(key)) : opts
245
+ end
246
+ end
247
+
248
+ def collation
249
+ arguments['collation']
250
+ end
251
+
252
+ def command
253
+ arguments['command']
254
+ end
255
+
256
+ def replacement
257
+ arguments['replacement']
258
+ end
259
+
260
+ def sort
261
+ arguments['sort']
262
+ end
263
+
264
+ def projection
265
+ arguments['projection']
266
+ end
267
+
268
+ def documents
269
+ arguments['documents']
270
+ end
271
+
272
+ def document
273
+ arguments['document']
274
+ end
275
+
276
+ def ordered
277
+ arguments['ordered']
278
+ end
279
+
280
+ def field_name
281
+ arguments['fieldName']
282
+ end
283
+
284
+ def filter
285
+ arguments['filter']
286
+ end
287
+
288
+ def pipeline
289
+ arguments['pipeline']
290
+ end
291
+
292
+ def array_filters
293
+ arguments['arrayFilters']
294
+ end
295
+
296
+ def batch_size
297
+ arguments['batchSize']
298
+ end
299
+
300
+ def session
301
+ arguments['session']
302
+ end
303
+
304
+ def requests
305
+ arguments['requests'].map do |request|
306
+ case request.keys.first
307
+ when 'insertOne' then
308
+ { insert_one: request['insertOne']['document'] }
309
+ when 'updateOne' then
310
+ update = request['updateOne']
311
+ { update_one: { filter: update['filter'], update: update['update'] } }
312
+ when 'name' then
313
+ bulk_request(request)
314
+ end
315
+ end
316
+ end
317
+
318
+ def bulk_request(request)
319
+ op_name = OPERATIONS[request['name']]
320
+ op = { op_name => {} }
321
+
322
+ op[op_name][:filter] = request['arguments']['filter'] if request['arguments']['filter']
323
+ op[op_name][:update] = request['arguments']['update'] if request['arguments']['update']
324
+ op[op_name][:upsert] = request['arguments']['upsert'] if request['arguments']['upsert']
325
+ op[op_name][:replacement] = request['arguments']['replacement'] if request['arguments']['replacement']
326
+ op[op_name][:array_filters] = request['arguments']['arrayFilters'] if request['arguments']['arrayFilters']
327
+ op[op_name] = request['arguments']['document'] if request['arguments']['document']
328
+ op
329
+ end
330
+
331
+ def upsert
332
+ arguments['upsert']
333
+ end
334
+
335
+ def return_document
336
+ case arguments['returnDocument']
337
+ when 'Before'
338
+ :before
339
+ when 'After'
340
+ :after
341
+ end
342
+ end
343
+
344
+ def update
345
+ arguments['update']
346
+ end
347
+
348
+ def arguments
349
+ @spec['arguments'] || {}
350
+ end
351
+
352
+ def modifiers
353
+ arguments['modifiers']
354
+ end
355
+
356
+ def read_concern
357
+ snakeize_hash(@spec['collectionOptions'] && @spec['collectionOptions']['readConcern'])
358
+ end
359
+
360
+ def write_concern
361
+ snakeize_hash(@spec['collectionOptions'] && @spec['collectionOptions']['writeConcern'])
362
+ end
363
+
364
+ def read_preference
365
+ snakeize_hash(arguments['readPreference'])
366
+ end
367
+
368
+ def collection_read_preference
369
+ snakeize_hash(@spec['collectionOptions'] && @spec['collectionOptions']['readPreference'])
370
+ end
371
+ end
372
+ end
373
+ end