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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE +1 -1
- data/README.md +3 -2
- data/lib/mongo.rb +2 -2
- data/lib/mongo/address.rb +10 -2
- data/lib/mongo/address/ipv4.rb +1 -1
- data/lib/mongo/address/ipv6.rb +26 -5
- data/lib/mongo/address/unix.rb +1 -1
- data/lib/mongo/auth.rb +10 -3
- data/lib/mongo/auth/cr.rb +4 -1
- data/lib/mongo/auth/cr/conversation.rb +4 -1
- data/lib/mongo/auth/ldap.rb +1 -1
- data/lib/mongo/auth/ldap/conversation.rb +1 -1
- data/lib/mongo/auth/roles.rb +1 -1
- data/lib/mongo/auth/scram.rb +24 -7
- data/lib/mongo/auth/scram/conversation.rb +52 -19
- data/lib/mongo/auth/stringprep.rb +114 -0
- data/lib/mongo/auth/stringprep/profiles/sasl.rb +73 -0
- data/lib/mongo/auth/stringprep/tables.rb +3232 -0
- data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +174 -0
- data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +1170 -0
- data/lib/mongo/auth/user.rb +14 -3
- data/lib/mongo/auth/user/view.rb +1 -1
- data/lib/mongo/auth/x509.rb +1 -1
- data/lib/mongo/auth/x509/conversation.rb +1 -1
- data/lib/mongo/bson.rb +1 -1
- data/lib/mongo/bulk_write.rb +8 -8
- data/lib/mongo/bulk_write/combineable.rb +1 -1
- data/lib/mongo/bulk_write/ordered_combiner.rb +1 -1
- data/lib/mongo/bulk_write/result.rb +1 -1
- data/lib/mongo/bulk_write/result_combiner.rb +4 -4
- data/lib/mongo/bulk_write/transformable.rb +1 -1
- data/lib/mongo/bulk_write/unordered_combiner.rb +1 -1
- data/lib/mongo/bulk_write/validatable.rb +1 -1
- data/lib/mongo/client.rb +115 -24
- data/lib/mongo/cluster.rb +17 -10
- data/lib/mongo/cluster/app_metadata.rb +7 -1
- data/lib/mongo/cluster/periodic_executor.rb +1 -1
- data/lib/mongo/cluster/reapers/socket_reaper.rb +1 -1
- data/lib/mongo/cluster/topology.rb +12 -2
- data/lib/mongo/cluster/topology/replica_set.rb +9 -1
- data/lib/mongo/cluster/topology/sharded.rb +1 -1
- data/lib/mongo/cluster/topology/single.rb +1 -1
- data/lib/mongo/cluster/topology/unknown.rb +1 -1
- data/lib/mongo/collection.rb +75 -19
- data/lib/mongo/collection/view.rb +1 -1
- data/lib/mongo/collection/view/aggregation.rb +1 -1
- data/lib/mongo/collection/view/builder.rb +1 -1
- data/lib/mongo/collection/view/builder/aggregation.rb +3 -3
- data/lib/mongo/collection/view/builder/find_command.rb +1 -1
- data/lib/mongo/collection/view/builder/flags.rb +1 -1
- data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
- data/lib/mongo/collection/view/builder/modifiers.rb +1 -1
- data/lib/mongo/collection/view/builder/op_query.rb +1 -1
- data/lib/mongo/collection/view/change_stream.rb +193 -17
- data/lib/mongo/collection/view/change_stream/retryable.rb +3 -20
- data/lib/mongo/collection/view/explainable.rb +1 -1
- data/lib/mongo/collection/view/immutable.rb +1 -1
- data/lib/mongo/collection/view/iterable.rb +2 -2
- data/lib/mongo/collection/view/map_reduce.rb +1 -1
- data/lib/mongo/collection/view/readable.rb +108 -29
- data/lib/mongo/collection/view/writable.rb +3 -3
- data/lib/mongo/cursor.rb +44 -4
- data/lib/mongo/cursor/builder.rb +1 -1
- data/lib/mongo/cursor/builder/get_more_command.rb +1 -1
- data/lib/mongo/cursor/builder/kill_cursors_command.rb +1 -1
- data/lib/mongo/cursor/builder/op_get_more.rb +1 -1
- data/lib/mongo/cursor/builder/op_kill_cursors.rb +1 -1
- data/lib/mongo/database.rb +46 -3
- data/lib/mongo/database/view.rb +11 -11
- data/lib/mongo/dbref.rb +1 -1
- data/lib/mongo/error.rb +57 -1
- data/lib/mongo/error/bulk_write_error.rb +2 -2
- data/lib/mongo/error/change_stream_resumable.rb +37 -0
- data/lib/mongo/error/closed_stream.rb +1 -1
- data/lib/mongo/error/extra_file_chunk.rb +1 -1
- data/lib/mongo/error/failed_stringprep_validation.rb +38 -0
- data/lib/mongo/error/file_not_found.rb +1 -1
- data/lib/mongo/error/insufficient_iteration_count.rb +38 -0
- data/lib/mongo/error/invalid_application_name.rb +1 -1
- data/lib/mongo/error/invalid_bulk_operation.rb +1 -1
- data/lib/mongo/error/invalid_bulk_operation_type.rb +1 -1
- data/lib/mongo/error/invalid_collection_name.rb +1 -1
- data/lib/mongo/error/invalid_database_name.rb +1 -1
- data/lib/mongo/error/invalid_document.rb +1 -1
- data/lib/mongo/error/invalid_file.rb +1 -1
- data/lib/mongo/error/invalid_file_revision.rb +1 -1
- data/lib/mongo/error/invalid_min_pool_size.rb +1 -1
- data/lib/mongo/error/invalid_nonce.rb +1 -1
- data/lib/mongo/error/invalid_read_option.rb +35 -0
- data/lib/mongo/error/invalid_replacement_document.rb +1 -1
- data/lib/mongo/error/invalid_server_preference.rb +1 -1
- data/lib/mongo/error/invalid_session.rb +1 -1
- data/lib/mongo/error/invalid_signature.rb +1 -1
- data/lib/mongo/error/invalid_transaction_operation.rb +82 -0
- data/lib/mongo/error/invalid_txt_record.rb +1 -1
- data/lib/mongo/error/invalid_update_document.rb +1 -1
- data/lib/mongo/error/invalid_uri.rb +1 -1
- data/lib/mongo/error/invalid_write_concern.rb +1 -1
- data/lib/mongo/error/max_bson_size.rb +1 -1
- data/lib/mongo/error/max_message_size.rb +1 -1
- data/lib/mongo/error/mismatched_domain.rb +1 -1
- data/lib/mongo/error/missing_file_chunk.rb +1 -1
- data/lib/mongo/error/missing_resume_token.rb +1 -1
- data/lib/mongo/error/multi_index_drop.rb +1 -1
- data/lib/mongo/error/need_primary_server.rb +1 -1
- data/lib/mongo/error/no_server_available.rb +1 -1
- data/lib/mongo/error/no_srv_records.rb +1 -1
- data/lib/mongo/error/operation_failure.rb +108 -14
- data/lib/mongo/error/parser.rb +50 -1
- data/lib/mongo/error/socket_error.rb +5 -2
- data/lib/mongo/error/socket_timeout_error.rb +5 -2
- data/lib/mongo/error/unchangeable_collection_option.rb +1 -1
- data/lib/mongo/error/unexpected_chunk_length.rb +1 -1
- data/lib/mongo/error/unexpected_response.rb +1 -1
- data/lib/mongo/error/unknown_payload_type.rb +1 -1
- data/lib/mongo/error/unsupported_array_filters.rb +1 -1
- data/lib/mongo/error/unsupported_collation.rb +1 -1
- data/lib/mongo/error/unsupported_features.rb +1 -1
- data/lib/mongo/error/unsupported_message_type.rb +1 -1
- data/lib/mongo/error/write_retryable.rb +27 -0
- data/lib/mongo/event.rb +10 -9
- data/lib/mongo/event/base.rb +33 -0
- data/lib/mongo/event/description_changed.rb +2 -2
- data/lib/mongo/event/listeners.rb +1 -1
- data/lib/mongo/event/member_discovered.rb +4 -2
- data/lib/mongo/event/primary_elected.rb +2 -2
- data/lib/mongo/event/publisher.rb +1 -1
- data/lib/mongo/event/standalone_discovered.rb +2 -2
- data/lib/mongo/event/subscriber.rb +1 -1
- data/lib/mongo/grid.rb +1 -1
- data/lib/mongo/grid/file.rb +1 -1
- data/lib/mongo/grid/file/chunk.rb +3 -3
- data/lib/mongo/grid/file/info.rb +26 -3
- data/lib/mongo/grid/fs_bucket.rb +1 -1
- data/lib/mongo/grid/stream.rb +1 -1
- data/lib/mongo/grid/stream/read.rb +1 -1
- data/lib/mongo/grid/stream/write.rb +1 -1
- data/lib/mongo/index.rb +1 -1
- data/lib/mongo/index/view.rb +1 -1
- data/lib/mongo/loggable.rb +1 -1
- data/lib/mongo/logger.rb +1 -1
- data/lib/mongo/monitoring.rb +99 -62
- data/lib/mongo/monitoring/command_log_subscriber.rb +2 -2
- data/lib/mongo/monitoring/event.rb +2 -1
- data/lib/mongo/monitoring/event/command_failed.rb +19 -6
- data/lib/mongo/monitoring/event/command_started.rb +14 -3
- data/lib/mongo/monitoring/event/command_succeeded.rb +5 -3
- data/lib/mongo/monitoring/event/secure.rb +1 -1
- data/lib/mongo/monitoring/event/server_closed.rb +2 -2
- data/lib/mongo/monitoring/event/server_description_changed.rb +2 -2
- data/lib/mongo/monitoring/event/server_opening.rb +11 -2
- data/lib/mongo/monitoring/event/topology_changed.rb +13 -2
- data/lib/mongo/monitoring/event/topology_closed.rb +2 -2
- data/lib/mongo/monitoring/event/topology_opening.rb +11 -2
- data/lib/mongo/monitoring/publishable.rb +10 -6
- data/lib/mongo/monitoring/sdam_log_subscriber.rb +1 -1
- data/lib/mongo/monitoring/server_closed_log_subscriber.rb +1 -1
- data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +1 -1
- data/lib/mongo/monitoring/server_opening_log_subscriber.rb +1 -1
- data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
- data/lib/mongo/monitoring/topology_opening_log_subscriber.rb +1 -1
- data/lib/mongo/operation/aggregate/op_msg.rb +3 -0
- data/lib/mongo/operation/create/op_msg.rb +9 -0
- data/lib/mongo/operation/create_index/op_msg.rb +9 -0
- data/lib/mongo/operation/create_user/command.rb +1 -1
- data/lib/mongo/operation/create_user/op_msg.rb +10 -1
- data/lib/mongo/operation/delete/op_msg.rb +3 -0
- data/lib/mongo/operation/distinct/op_msg.rb +9 -0
- data/lib/mongo/operation/drop/op_msg.rb +9 -0
- data/lib/mongo/operation/drop_database/op_msg.rb +9 -0
- data/lib/mongo/operation/drop_index/op_msg.rb +9 -0
- data/lib/mongo/operation/explain/op_msg.rb +3 -0
- data/lib/mongo/operation/find/op_msg.rb +3 -0
- data/lib/mongo/operation/get_more.rb +1 -1
- data/lib/mongo/operation/get_more/command.rb +1 -1
- data/lib/mongo/operation/get_more/legacy.rb +1 -1
- data/lib/mongo/operation/get_more/op_msg.rb +3 -0
- data/lib/mongo/operation/indexes/op_msg.rb +3 -0
- data/lib/mongo/operation/indexes/result.rb +1 -1
- data/lib/mongo/operation/insert/bulk_result.rb +32 -2
- data/lib/mongo/operation/insert/op_msg.rb +3 -0
- data/lib/mongo/operation/insert/result.rb +1 -1
- data/lib/mongo/operation/kill_cursors/op_msg.rb +9 -0
- data/lib/mongo/operation/list_collections/op_msg.rb +3 -0
- data/lib/mongo/operation/list_collections/result.rb +5 -1
- data/lib/mongo/operation/map_reduce/op_msg.rb +3 -0
- data/lib/mongo/operation/map_reduce/result.rb +1 -1
- data/lib/mongo/operation/parallel_scan/op_msg.rb +3 -0
- data/lib/mongo/operation/remove_user/op_msg.rb +9 -0
- data/lib/mongo/operation/result.rb +27 -14
- data/lib/mongo/operation/shared/executable.rb +1 -0
- data/lib/mongo/operation/shared/sessions_supported.rb +78 -7
- data/lib/mongo/operation/shared/specifiable.rb +18 -2
- data/lib/mongo/operation/shared/write_concern_supported.rb +1 -1
- data/lib/mongo/operation/update/op_msg.rb +3 -0
- data/lib/mongo/operation/update_user/command.rb +1 -1
- data/lib/mongo/operation/update_user/op_msg.rb +10 -1
- data/lib/mongo/operation/users_info/op_msg.rb +3 -0
- data/lib/mongo/options.rb +1 -1
- data/lib/mongo/options/mapper.rb +1 -1
- data/lib/mongo/options/redacted.rb +1 -1
- data/lib/mongo/protocol/bit_vector.rb +1 -1
- data/lib/mongo/protocol/compressed.rb +1 -1
- data/lib/mongo/protocol/delete.rb +1 -1
- data/lib/mongo/protocol/get_more.rb +7 -7
- data/lib/mongo/protocol/insert.rb +1 -1
- data/lib/mongo/protocol/kill_cursors.rb +1 -1
- data/lib/mongo/protocol/message.rb +5 -5
- data/lib/mongo/protocol/msg.rb +9 -7
- data/lib/mongo/protocol/query.rb +1 -1
- data/lib/mongo/protocol/registry.rb +1 -1
- data/lib/mongo/protocol/reply.rb +10 -10
- data/lib/mongo/protocol/serializers.rb +1 -1
- data/lib/mongo/protocol/update.rb +1 -1
- data/lib/mongo/retryable.rb +22 -14
- data/lib/mongo/server.rb +1 -1
- data/lib/mongo/server/connectable.rb +1 -1
- data/lib/mongo/server/connection.rb +16 -4
- data/lib/mongo/server/connection_pool.rb +1 -1
- data/lib/mongo/server/connection_pool/queue.rb +1 -1
- data/lib/mongo/server/context.rb +1 -1
- data/lib/mongo/server/description.rb +14 -2
- data/lib/mongo/server/description/features.rb +10 -9
- data/lib/mongo/server/description/inspector.rb +1 -1
- data/lib/mongo/server/description/inspector/description_changed.rb +1 -1
- data/lib/mongo/server/description/inspector/member_discovered.rb +1 -1
- data/lib/mongo/server/description/inspector/primary_elected.rb +1 -1
- data/lib/mongo/server/description/inspector/standalone_discovered.rb +1 -1
- data/lib/mongo/server/monitor.rb +15 -3
- data/lib/mongo/server/monitor/connection.rb +1 -1
- data/lib/mongo/server_selector.rb +1 -1
- data/lib/mongo/server_selector/nearest.rb +1 -1
- data/lib/mongo/server_selector/primary.rb +1 -1
- data/lib/mongo/server_selector/primary_preferred.rb +1 -1
- data/lib/mongo/server_selector/secondary.rb +1 -1
- data/lib/mongo/server_selector/secondary_preferred.rb +1 -1
- data/lib/mongo/server_selector/selectable.rb +7 -2
- data/lib/mongo/session.rb +389 -12
- data/lib/mongo/session/server_session.rb +7 -2
- data/lib/mongo/session/session_pool.rb +1 -1
- data/lib/mongo/socket.rb +1 -1
- data/lib/mongo/socket/ssl.rb +1 -1
- data/lib/mongo/socket/tcp.rb +1 -1
- data/lib/mongo/socket/unix.rb +1 -1
- data/lib/mongo/uri.rb +6 -4
- data/lib/mongo/uri/srv_protocol.rb +1 -1
- data/lib/mongo/version.rb +2 -2
- data/lib/mongo/write_concern.rb +1 -1
- data/lib/mongo/write_concern/acknowledged.rb +1 -1
- data/lib/mongo/write_concern/normalizable.rb +1 -1
- data/lib/mongo/write_concern/unacknowledged.rb +1 -1
- data/mongo.gemspec +4 -1
- data/spec/atlas/atlas_connectivity_spec.rb +54 -0
- data/spec/integration/bulk_insert_spec.rb +78 -0
- data/spec/integration/change_stream_spec.rb +365 -0
- data/spec/integration/command_monitoring_spec.rb +92 -0
- data/spec/lite_spec_helper.rb +63 -0
- data/spec/mongo/address/ipv6_spec.rb +29 -1
- data/spec/mongo/address_spec.rb +34 -0
- data/spec/mongo/auth/scram/conversation_spec.rb +326 -120
- data/spec/mongo/auth/scram/negotiation_spec.rb +574 -0
- data/spec/mongo/auth/scram_spec.rb +107 -38
- data/spec/mongo/auth/stringprep/profiles/sasl_spec.rb +113 -0
- data/spec/mongo/auth/stringprep_spec.rb +188 -0
- data/spec/mongo/auth/user/view_spec.rb +5 -2
- data/spec/mongo/auth/user_spec.rb +1 -1
- data/spec/mongo/bulk_write/result_spec.rb +120 -0
- data/spec/mongo/bulk_write_spec.rb +42 -2
- data/spec/mongo/client_spec.rb +121 -9
- data/spec/mongo/cluster/app_metadata_spec.rb +14 -1
- data/spec/mongo/cluster/topology_spec.rb +1 -23
- data/spec/mongo/collection/view/change_stream_spec.rb +62 -180
- data/spec/mongo/collection_spec.rb +45 -12
- data/spec/mongo/cursor/builder/get_more_command_spec.rb +7 -7
- data/spec/mongo/cursor_spec.rb +2 -2
- data/spec/mongo/database_spec.rb +3 -3
- data/spec/mongo/docs_examples_spec.rb +194 -0
- data/spec/mongo/error/operation_failure_spec.rb +152 -0
- data/spec/mongo/error/parser_spec.rb +127 -0
- data/spec/mongo/grid/fs_bucket_spec.rb +32 -0
- data/spec/mongo/grid/stream/write_spec.rb +40 -1
- data/spec/mongo/monitoring/event/command_failed_spec.rb +30 -0
- data/spec/mongo/monitoring/event/command_started_spec.rb +26 -4
- data/spec/mongo/monitoring/event/command_succeeded_spec.rb +29 -7
- data/spec/mongo/monitoring_spec.rb +28 -3
- data/spec/mongo/protocol/get_more_spec.rb +2 -2
- data/spec/mongo/retryable_spec.rb +252 -34
- data/spec/mongo/retryable_writes_spec.rb +468 -544
- data/spec/mongo/server/connection_spec.rb +5 -5
- data/spec/mongo/server/description_spec.rb +23 -6
- data/spec/mongo/session/server_session_spec.rb +2 -2
- data/spec/mongo/session/session_pool_spec.rb +2 -2
- data/spec/mongo/transactions_examples_spec.rb +227 -0
- data/spec/mongo/transactions_spec.rb +44 -0
- data/spec/spec_helper.rb +135 -49
- data/spec/spec_tests/change_streams_spec.rb +42 -0
- data/spec/{mongo → spec_tests}/command_monitoring_spec.rb +8 -2
- data/spec/{mongo → spec_tests}/connection_string_spec.rb +1 -1
- data/spec/{mongo → spec_tests}/crud_spec.rb +5 -7
- data/spec/{mongo → spec_tests}/dns_seedlist_discovery_spec.rb +1 -1
- data/spec/{mongo → spec_tests}/gridfs_spec.rb +0 -0
- data/spec/{mongo → spec_tests}/max_staleness_spec.rb +0 -0
- data/spec/spec_tests/retryable_writes_spec.rb +78 -0
- data/spec/{mongo → spec_tests}/sdam_monitoring_spec.rb +4 -3
- data/spec/{mongo → spec_tests}/sdam_spec.rb +7 -7
- data/spec/{mongo → spec_tests}/server_selection_rtt_spec.rb +0 -0
- data/spec/{mongo → spec_tests}/server_selection_spec.rb +0 -0
- data/spec/support/authorization.rb +18 -6
- data/spec/support/change_streams.rb +265 -0
- data/spec/support/change_streams/operation.rb +62 -0
- data/spec/support/change_streams_tests/change-streams-errors.yml +53 -0
- data/spec/support/change_streams_tests/change-streams.yml +299 -0
- data/spec/support/command_monitoring.rb +1 -1
- data/spec/support/command_monitoring/bulkWrite.yml +4 -28
- data/spec/support/command_monitoring/command.yml +19 -0
- data/spec/support/command_monitoring/find.yml +17 -19
- data/spec/support/command_monitoring/insertMany.yml +2 -8
- data/spec/support/command_monitoring/unacknowledgedBulkWrite.yml +34 -0
- data/spec/support/connection_string.rb +1 -1
- data/spec/support/constraints.rb +56 -0
- data/spec/support/crud.rb +9 -4
- data/spec/support/crud/read.rb +24 -3
- data/spec/support/crud/write.rb +7 -2
- data/spec/support/crud_tests/read/count-collation.yml +12 -2
- data/spec/support/crud_tests/read/count.yml +43 -5
- data/spec/support/gridfs.rb +1 -1
- data/spec/support/primary_socket.rb +21 -0
- data/spec/support/retryable_writes_tests/bulkWrite-serverErrors.yml +90 -0
- data/spec/support/retryable_writes_tests/bulkWrite.yml +99 -1
- data/spec/support/retryable_writes_tests/deleteOne-serverErrors.yml +50 -0
- data/spec/support/retryable_writes_tests/deleteOne.yml +10 -1
- data/spec/support/retryable_writes_tests/findOneAndDelete-serverErrors.yml +50 -0
- data/spec/support/retryable_writes_tests/findOneAndDelete.yml +39 -30
- data/spec/support/retryable_writes_tests/findOneAndReplace-serverErrors.yml +54 -0
- data/spec/support/retryable_writes_tests/findOneAndReplace.yml +9 -0
- data/spec/support/retryable_writes_tests/findOneAndUpdate-serverErrors.yml +54 -0
- data/spec/support/retryable_writes_tests/findOneAndUpdate.yml +9 -0
- data/spec/support/retryable_writes_tests/insertMany-serverErrors.yml +59 -0
- data/spec/support/retryable_writes_tests/insertMany.yml +11 -6
- data/spec/support/retryable_writes_tests/insertOne-serverErrors.yml +471 -0
- data/spec/support/retryable_writes_tests/insertOne.yml +9 -0
- data/spec/support/retryable_writes_tests/replaceOne-serverErrors.yml +58 -0
- data/spec/support/retryable_writes_tests/replaceOne.yml +9 -0
- data/spec/support/retryable_writes_tests/updateOne-serverErrors.yml +58 -0
- data/spec/support/retryable_writes_tests/updateOne.yml +71 -53
- data/spec/support/sdam/rs/normalize_case_me.yml +100 -0
- data/spec/support/sdam/sharded/compatible.yml +38 -0
- data/spec/support/sdam/sharded/mongos_disconnect.yml +9 -3
- data/spec/support/sdam/sharded/multiple_mongoses.yml +6 -2
- data/spec/support/sdam/sharded/non_mongos_removed.yml +6 -2
- data/spec/support/sdam/sharded/too_new.yml +36 -0
- data/spec/support/sdam/sharded/too_old.yml +36 -0
- data/spec/support/sdam/single/compatible.yml +26 -0
- data/spec/support/sdam/single/direct_connection_external_ip.yml +3 -1
- data/spec/support/sdam/single/direct_connection_mongos.yml +3 -1
- data/spec/support/sdam/single/direct_connection_rsarbiter.yml +3 -1
- data/spec/support/sdam/single/direct_connection_rsprimary.yml +3 -1
- data/spec/support/sdam/single/direct_connection_rssecondary.yml +3 -1
- data/spec/support/sdam/single/direct_connection_slave.yml +3 -1
- data/spec/support/sdam/single/direct_connection_standalone.yml +3 -1
- data/spec/support/sdam/single/not_ok_response.yml +6 -2
- data/spec/support/sdam/single/standalone_removed.yml +3 -1
- data/spec/support/sdam/single/too_new.yml +26 -0
- data/spec/support/sdam/single/too_old.yml +24 -0
- data/spec/support/shared/session.rb +107 -0
- data/spec/support/transactions.rb +391 -0
- data/spec/support/transactions/operation.rb +373 -0
- data/spec/support/transactions_tests/abort.yml +403 -0
- data/spec/support/transactions_tests/bulk.yml +267 -0
- data/spec/support/transactions_tests/causal-consistency.yml +173 -0
- data/spec/support/transactions_tests/commit.yml +593 -0
- data/spec/support/transactions_tests/delete.yml +184 -0
- data/spec/support/transactions_tests/error-labels.yml +948 -0
- data/spec/support/transactions_tests/errors.yml +125 -0
- data/spec/support/transactions_tests/findOneAndDelete.yml +126 -0
- data/spec/support/transactions_tests/findOneAndReplace.yml +140 -0
- data/spec/support/transactions_tests/findOneAndUpdate.yml +228 -0
- data/spec/support/transactions_tests/insert.yml +264 -0
- data/spec/support/transactions_tests/isolation.yml +125 -0
- data/spec/support/transactions_tests/read-pref.yml +340 -0
- data/spec/support/transactions_tests/reads.yml +298 -0
- data/spec/support/transactions_tests/retryable-abort.yml +1292 -0
- data/spec/support/transactions_tests/retryable-commit.yml +1332 -0
- data/spec/support/transactions_tests/retryable-writes.yml +208 -0
- data/spec/support/transactions_tests/run-command.yml +189 -0
- data/spec/support/transactions_tests/transaction-options.yml +877 -0
- data/spec/support/transactions_tests/update.yml +246 -0
- data/spec/support/transactions_tests/write-concern.yml +236 -0
- metadata +494 -359
- metadata.gz.sig +0 -0
- 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
|