mongo 2.18.1 → 2.18.3

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 (139) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +1 -1
  4. data/lib/mongo/bulk_write.rb +6 -4
  5. data/lib/mongo/client.rb +1 -1
  6. data/lib/mongo/collection/view/iterable.rb +15 -0
  7. data/lib/mongo/collection/view/writable.rb +0 -2
  8. data/lib/mongo/collection/view.rb +1 -0
  9. data/lib/mongo/collection.rb +150 -45
  10. data/lib/mongo/crypt/auto_encrypter.rb +1 -0
  11. data/lib/mongo/crypt/explicit_encrypter.rb +1 -0
  12. data/lib/mongo/crypt/kms.rb +0 -1
  13. data/lib/mongo/crypt.rb +11 -0
  14. data/lib/mongo/error/invalid_read_option.rb +1 -1
  15. data/lib/mongo/grid/file/chunk.rb +2 -1
  16. data/lib/mongo/grid/file/info.rb +2 -1
  17. data/lib/mongo/operation/aggregate.rb +1 -2
  18. data/lib/mongo/operation/collections_info.rb +3 -15
  19. data/lib/mongo/operation/command.rb +1 -2
  20. data/lib/mongo/operation/count.rb +1 -2
  21. data/lib/mongo/operation/create.rb +1 -2
  22. data/lib/mongo/operation/create_index.rb +1 -2
  23. data/lib/mongo/operation/create_user.rb +1 -2
  24. data/lib/mongo/operation/delete.rb +0 -1
  25. data/lib/mongo/operation/distinct.rb +1 -2
  26. data/lib/mongo/operation/drop.rb +1 -2
  27. data/lib/mongo/operation/drop_database.rb +1 -2
  28. data/lib/mongo/operation/drop_index.rb +1 -2
  29. data/lib/mongo/operation/explain.rb +1 -3
  30. data/lib/mongo/operation/find/builder.rb +0 -1
  31. data/lib/mongo/operation/find.rb +1 -3
  32. data/lib/mongo/operation/get_more.rb +1 -3
  33. data/lib/mongo/operation/indexes.rb +1 -17
  34. data/lib/mongo/operation/insert.rb +0 -1
  35. data/lib/mongo/operation/kill_cursors.rb +1 -2
  36. data/lib/mongo/operation/list_collections.rb +1 -2
  37. data/lib/mongo/operation/map_reduce.rb +1 -2
  38. data/lib/mongo/operation/parallel_scan.rb +1 -2
  39. data/lib/mongo/operation/remove_user.rb +1 -2
  40. data/lib/mongo/operation/shared/{polymorphic_operation.rb → op_msg_executable.rb} +11 -6
  41. data/lib/mongo/operation/update.rb +0 -1
  42. data/lib/mongo/operation/update_user.rb +1 -2
  43. data/lib/mongo/operation/users_info.rb +1 -2
  44. data/lib/mongo/operation/write_command.rb +1 -2
  45. data/lib/mongo/operation.rb +1 -3
  46. data/lib/mongo/protocol/bit_vector.rb +3 -1
  47. data/lib/mongo/protocol/caching_hash.rb +3 -20
  48. data/lib/mongo/protocol/message.rb +4 -8
  49. data/lib/mongo/protocol/msg.rb +1 -0
  50. data/lib/mongo/protocol/serializers.rb +24 -17
  51. data/lib/mongo/protocol.rb +0 -3
  52. data/lib/mongo/query_cache.rb +20 -20
  53. data/lib/mongo/server/app_metadata/environment.rb +255 -0
  54. data/lib/mongo/server/app_metadata/truncator.rb +142 -0
  55. data/lib/mongo/server/app_metadata.rb +29 -42
  56. data/lib/mongo/session.rb +1 -1
  57. data/lib/mongo/version.rb +1 -1
  58. data/spec/integration/command_spec.rb +1 -23
  59. data/spec/integration/connection/faas_env_spec.rb +63 -0
  60. data/spec/integration/find_options_spec.rb +227 -0
  61. data/spec/integration/ocsp_verifier_spec.rb +1 -1
  62. data/spec/lite_spec_helper.rb +9 -0
  63. data/spec/mongo/address_spec.rb +1 -1
  64. data/spec/mongo/client_construction_spec.rb +7 -7
  65. data/spec/mongo/client_spec.rb +1 -9
  66. data/spec/mongo/cluster_spec.rb +2 -2
  67. data/spec/mongo/collection_crud_spec.rb +56 -0
  68. data/spec/mongo/collection_spec.rb +11 -1
  69. data/spec/mongo/crypt/kms_spec.rb +12 -9
  70. data/spec/mongo/crypt_spec.rb +21 -0
  71. data/spec/mongo/index/view_spec.rb +1 -0
  72. data/spec/mongo/protocol/caching_hash_spec.rb +0 -45
  73. data/spec/mongo/protocol/msg_spec.rb +2 -4
  74. data/spec/mongo/server/app_metadata/environment_spec.rb +193 -0
  75. data/spec/mongo/server/app_metadata/truncator_spec.rb +158 -0
  76. data/spec/mongo/server/app_metadata_spec.rb +33 -47
  77. data/spec/mongo/socket/ssl_spec.rb +2 -8
  78. data/spec/runners/crud/requirement.rb +2 -2
  79. data/spec/shared/lib/mrss/docker_runner.rb +4 -0
  80. data/spec/shared/lib/mrss/lite_constraints.rb +8 -0
  81. data/spec/shared/lib/mrss/server_version_registry.rb +16 -23
  82. data/spec/shared/share/Dockerfile.erb +24 -19
  83. data/spec/shared/shlib/server.sh +31 -7
  84. data/spec/shared/shlib/set_env.sh +4 -4
  85. data/spec/solo/clean_exit_spec.rb +3 -10
  86. data/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml +15 -6
  87. data/spec/spec_tests/data/command_monitoring_unified/redacted-commands.yml +8 -0
  88. data/spec/support/aws_utils.rb +3 -3
  89. data/spec/support/certificates/atlas-ocsp-ca.crt +67 -67
  90. data/spec/support/certificates/atlas-ocsp.crt +103 -103
  91. data/spec/support/shared/app_metadata.rb +14 -2
  92. data.tar.gz.sig +0 -0
  93. metadata +1203 -1239
  94. metadata.gz.sig +0 -0
  95. data/lib/mongo/operation/aggregate/command.rb +0 -55
  96. data/lib/mongo/operation/collections_info/command.rb +0 -48
  97. data/lib/mongo/operation/command/command.rb +0 -41
  98. data/lib/mongo/operation/count/command.rb +0 -47
  99. data/lib/mongo/operation/create/command.rb +0 -47
  100. data/lib/mongo/operation/create_index/command.rb +0 -61
  101. data/lib/mongo/operation/create_user/command.rb +0 -46
  102. data/lib/mongo/operation/delete/command.rb +0 -52
  103. data/lib/mongo/operation/distinct/command.rb +0 -47
  104. data/lib/mongo/operation/drop/command.rb +0 -41
  105. data/lib/mongo/operation/drop_database/command.rb +0 -41
  106. data/lib/mongo/operation/drop_index/command.rb +0 -45
  107. data/lib/mongo/operation/explain/command.rb +0 -58
  108. data/lib/mongo/operation/explain/legacy.rb +0 -52
  109. data/lib/mongo/operation/find/builder/legacy.rb +0 -123
  110. data/lib/mongo/operation/find/command.rb +0 -51
  111. data/lib/mongo/operation/find/legacy/result.rb +0 -46
  112. data/lib/mongo/operation/find/legacy.rb +0 -52
  113. data/lib/mongo/operation/get_more/command.rb +0 -43
  114. data/lib/mongo/operation/get_more/legacy.rb +0 -39
  115. data/lib/mongo/operation/indexes/command.rb +0 -42
  116. data/lib/mongo/operation/indexes/legacy.rb +0 -48
  117. data/lib/mongo/operation/insert/command.rb +0 -55
  118. data/lib/mongo/operation/kill_cursors/command.rb +0 -48
  119. data/lib/mongo/operation/list_collections/command.rb +0 -46
  120. data/lib/mongo/operation/map_reduce/command.rb +0 -51
  121. data/lib/mongo/operation/parallel_scan/command.rb +0 -57
  122. data/lib/mongo/operation/remove_user/command.rb +0 -46
  123. data/lib/mongo/operation/shared/op_msg_or_command.rb +0 -41
  124. data/lib/mongo/operation/shared/op_msg_or_find_command.rb +0 -44
  125. data/lib/mongo/operation/update/command.rb +0 -53
  126. data/lib/mongo/operation/update_user/command.rb +0 -45
  127. data/lib/mongo/operation/users_info/command.rb +0 -46
  128. data/lib/mongo/operation/write_command/command.rb +0 -51
  129. data/lib/mongo/protocol/delete.rb +0 -172
  130. data/lib/mongo/protocol/insert.rb +0 -181
  131. data/lib/mongo/protocol/update.rb +0 -214
  132. data/spec/mongo/operation/delete/command_spec.rb +0 -115
  133. data/spec/mongo/operation/find/legacy_spec.rb +0 -131
  134. data/spec/mongo/operation/get_more_spec.rb +0 -63
  135. data/spec/mongo/operation/insert/command_spec.rb +0 -118
  136. data/spec/mongo/operation/update/command_spec.rb +0 -122
  137. data/spec/mongo/protocol/delete_spec.rb +0 -185
  138. data/spec/mongo/protocol/insert_spec.rb +0 -179
  139. data/spec/mongo/protocol/update_spec.rb +0 -204
@@ -51,9 +51,11 @@ module Mongo
51
51
  #
52
52
  # @param buffer [ String ] Buffer to receive the serialized value.
53
53
  # @param value [ String ] Header value to be serialized.
54
+ # @param [ true, false ] validating_keys Whether keys should be validated when serializing.
55
+ # This option is deprecated and will not be used. It will removed in version 3.0.
54
56
  #
55
57
  # @return [ String ] Buffer with serialized value.
56
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
58
+ def self.serialize(buffer, value, validating_keys = nil)
57
59
  buffer.put_bytes(value.pack(HEADER_PACK))
58
60
  end
59
61
 
@@ -80,7 +82,7 @@ module Mongo
80
82
  # @param value [ String ] The string to be serialized.
81
83
  #
82
84
  # @return [ String ] Buffer with serialized value.
83
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
85
+ def self.serialize(buffer, value, validating_keys = nil)
84
86
  buffer.put_cstring(value)
85
87
  end
86
88
  end
@@ -96,7 +98,7 @@ module Mongo
96
98
  # @param value [ Fixnum ] Ignored value.
97
99
  #
98
100
  # @return [ String ] Buffer with serialized value.
99
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
101
+ def self.serialize(buffer, value, validating_keys = nil)
100
102
  buffer.put_int32(ZERO)
101
103
  end
102
104
  end
@@ -112,7 +114,7 @@ module Mongo
112
114
  # @param value [ Integer | BSON::Int32 ] 32-bit integer to be serialized.
113
115
  #
114
116
  # @return [String] Buffer with serialized value.
115
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
117
+ def self.serialize(buffer, value, validating_keys = nil)
116
118
  if value.is_a?(BSON::Int32)
117
119
  if value.respond_to?(:value)
118
120
  # bson-ruby >= 4.6.0
@@ -146,7 +148,7 @@ module Mongo
146
148
  # @param value [ Integer | BSON::Int64 ] 64-bit integer to be serialized.
147
149
  #
148
150
  # @return [ String ] Buffer with serialized value.
149
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
151
+ def self.serialize(buffer, value, validating_keys = nil)
150
152
  if value.is_a?(BSON::Int64)
151
153
  if value.respond_to?(:value)
152
154
  # bson-ruby >= 4.6.0
@@ -182,19 +184,20 @@ module Mongo
182
184
  # @param [ Array<Hash, BSON::Document> ] value The sections to be serialized.
183
185
  # @param [ Fixnum ] max_bson_size The max bson size of documents in the sections.
184
186
  # @param [ true, false ] validating_keys Whether to validate document keys.
187
+ # This option is deprecated and will not be used. It will removed in version 3.0.
185
188
  #
186
189
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
187
190
  #
188
191
  # @since 2.5.0
189
- def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
192
+ def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
190
193
  value.each do |section|
191
194
  case section[:type]
192
195
  when PayloadZero::TYPE
193
- PayloadZero.serialize(buffer, section[:payload], max_bson_size, false)
196
+ PayloadZero.serialize(buffer, section[:payload], max_bson_size)
194
197
  when nil
195
- PayloadZero.serialize(buffer, section[:payload], max_bson_size, false)
198
+ PayloadZero.serialize(buffer, section[:payload], max_bson_size)
196
199
  when PayloadOne::TYPE
197
- PayloadOne.serialize(buffer, section[:payload], max_bson_size, validating_keys)
200
+ PayloadOne.serialize(buffer, section[:payload], max_bson_size)
198
201
  else
199
202
  raise Error::UnknownPayloadType.new(section[:type])
200
203
  end
@@ -259,13 +262,14 @@ module Mongo
259
262
  # @param [ BSON::Document, Hash ] value The object to serialize.
260
263
  # @param [ Fixnum ] max_bson_size The max bson size of documents in the section.
261
264
  # @param [ true, false ] validating_keys Whether to validate document keys.
265
+ # This option is deprecated and will not be used. It will removed in version 3.0.
262
266
  #
263
267
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
264
268
  #
265
269
  # @since 2.5.0
266
- def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
270
+ def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
267
271
  buffer.put_byte(TYPE_BYTE)
268
- Serializers::Document.serialize(buffer, value, max_bson_size, validating_keys)
272
+ Serializers::Document.serialize(buffer, value, max_bson_size)
269
273
  end
270
274
 
271
275
  # Deserializes a section of payload type 0 of an OP_MSG from the IO stream.
@@ -307,17 +311,18 @@ module Mongo
307
311
  # @param [ BSON::Document, Hash ] value The object to serialize.
308
312
  # @param [ Fixnum ] max_bson_size The max bson size of documents in the section.
309
313
  # @param [ true, false ] validating_keys Whether to validate document keys.
314
+ # This option is deprecated and will not be used. It will removed in version 3.0.
310
315
  #
311
316
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
312
317
  #
313
318
  # @since 2.5.0
314
- def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
319
+ def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
315
320
  buffer.put_byte(TYPE_BYTE)
316
321
  start = buffer.length
317
322
  buffer.put_int32(0) # hold for size
318
323
  buffer.put_cstring(value[:identifier])
319
324
  value[:sequence].each do |document|
320
- Document.serialize(buffer, document, max_bson_size, validating_keys)
325
+ Document.serialize(buffer, document, max_bson_size)
321
326
  end
322
327
  buffer.replace_int32(start, buffer.length - start)
323
328
  end
@@ -356,9 +361,9 @@ module Mongo
356
361
  # @param value [ Hash ] Document to serialize as BSON.
357
362
  #
358
363
  # @return [ String ] Buffer with serialized value.
359
- def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
364
+ def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
360
365
  start_size = buffer.length
361
- value.to_bson(buffer, validating_keys)
366
+ value.to_bson(buffer)
362
367
  serialized_size = buffer.length - start_size
363
368
  if max_bson_size && serialized_size > max_bson_size
364
369
  raise Error::MaxBSONSize,
@@ -401,11 +406,12 @@ module Mongo
401
406
  # @param [ BSON::ByteBuffer ] buffer Buffer to receive the single byte.
402
407
  # @param [ String ] value The byte to write to the buffer.
403
408
  # @param [ true, false ] validating_keys Whether to validate keys.
409
+ # This option is deprecated and will not be used. It will removed in version 3.0.
404
410
  #
405
411
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
406
412
  #
407
413
  # @since 2.5.0
408
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
414
+ def self.serialize(buffer, value, validating_keys = nil)
409
415
  buffer.put_byte(value)
410
416
  end
411
417
 
@@ -432,11 +438,12 @@ module Mongo
432
438
  # @param [ BSON::ByteBuffer ] buffer Buffer to receive the bytes.
433
439
  # @param [ String ] value The bytes to write to the buffer.
434
440
  # @param [ true, false ] validating_keys Whether to validate keys.
441
+ # This option is deprecated and will not be used. It will removed in version 3.0.
435
442
  #
436
443
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
437
444
  #
438
445
  # @since 2.5.0
439
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
446
+ def self.serialize(buffer, value, validating_keys = nil)
440
447
  buffer.put_bytes(value)
441
448
  end
442
449
 
@@ -10,12 +10,9 @@ require 'mongo/protocol/caching_hash'
10
10
 
11
11
  # Client Requests
12
12
  require 'mongo/protocol/compressed'
13
- require 'mongo/protocol/delete'
14
13
  require 'mongo/protocol/get_more'
15
- require 'mongo/protocol/insert'
16
14
  require 'mongo/protocol/kill_cursors'
17
15
  require 'mongo/protocol/query'
18
- require 'mongo/protocol/update'
19
16
  require 'mongo/protocol/msg'
20
17
 
21
18
  # Server Responses
@@ -114,24 +114,24 @@ module Mongo
114
114
  #
115
115
  # @param [ Mongo::CachingCursor ] cursor The CachingCursor instance to store.
116
116
  #
117
- # @option opts [ String | nil ] namespace The namespace of the query,
117
+ # @option opts [ String | nil ] :namespace The namespace of the query,
118
118
  # in the format "database_name.collection_name".
119
- # @option opts [ Array, Hash ] selector The selector passed to the query.
119
+ # @option opts [ Array, Hash ] :selector The selector passed to the query.
120
120
  # For most queries, this will be a Hash, but for aggregations, this
121
121
  # will be an Array representing the aggregation pipeline. May not be nil.
122
- # @option opts [ Integer | nil ] skip The skip value of the query.
123
- # @option opts [ Hash | nil ] sort The order of the query results
122
+ # @option opts [ Integer | nil ] :skip The skip value of the query.
123
+ # @option opts [ Hash | nil ] :sort The order of the query results
124
124
  # (e.g. { name: -1 }).
125
- # @option opts [ Integer | nil ] limit The limit value of the query.
126
- # @option opts [ Hash | nil ] projection The projection of the query
125
+ # @option opts [ Integer | nil ] :limit The limit value of the query.
126
+ # @option opts [ Hash | nil ] :projection The projection of the query
127
127
  # results (e.g. { name: 1 }).
128
- # @option opts [ Hash | nil ] collation The collation of the query
128
+ # @option opts [ Hash | nil ] :collation The collation of the query
129
129
  # (e.g. { "locale" => "fr_CA" }).
130
- # @option opts [ Hash | nil ] read_concern The read concern of the query
130
+ # @option opts [ Hash | nil ] :read_concern The read concern of the query
131
131
  # (e.g. { level: :majority }).
132
- # @option opts [ Hash | nil ] read_preference The read preference of
132
+ # @option opts [ Hash | nil ] :read_preference The read preference of
133
133
  # the query (e.g. { mode: :secondary }).
134
- # @option opts [ Boolean | nil ] multi_collection Whether the query
134
+ # @option opts [ Boolean | nil ] :multi_collection Whether the query
135
135
  # results could potentially come from multiple collections. When true,
136
136
  # these results will be stored under the nil namespace key and cleared
137
137
  # on every write command.
@@ -152,24 +152,24 @@ module Mongo
152
152
  # For the given query options, retrieve a cached cursor that can be used
153
153
  # to obtain the correct query results, if one exists in the cache.
154
154
  #
155
- # @option opts [ String | nil ] namespace The namespace of the query,
155
+ # @option opts [ String | nil ] :namespace The namespace of the query,
156
156
  # in the format "database_name.collection_name".
157
- # @option opts [ Array, Hash ] selector The selector passed to the query.
157
+ # @option opts [ Array, Hash ] :selector The selector passed to the query.
158
158
  # For most queries, this will be a Hash, but for aggregations, this
159
159
  # will be an Array representing the aggregation pipeline. May not be nil.
160
- # @option opts [ Integer | nil ] skip The skip value of the query.
161
- # @option opts [ Hash | nil ] sort The order of the query results
160
+ # @option opts [ Integer | nil ] :skip The skip value of the query.
161
+ # @option opts [ Hash | nil ] :sort The order of the query results
162
162
  # (e.g. { name: -1 }).
163
- # @option opts [ Integer | nil ] limit The limit value of the query.
164
- # @option opts [ Hash | nil ] projection The projection of the query
163
+ # @option opts [ Integer | nil ] :limit The limit value of the query.
164
+ # @option opts [ Hash | nil ] :projection The projection of the query
165
165
  # results (e.g. { name: 1 }).
166
- # @option opts [ Hash | nil ] collation The collation of the query
166
+ # @option opts [ Hash | nil ] :collation The collation of the query
167
167
  # (e.g. { "locale" => "fr_CA" }).
168
- # @option opts [ Hash | nil ] read_concern The read concern of the query
168
+ # @option opts [ Hash | nil ] :read_concern The read concern of the query
169
169
  # (e.g. { level: :majority }).
170
- # @option opts [ Hash | nil ] read_preference The read preference of
170
+ # @option opts [ Hash | nil ] :read_preference The read preference of
171
171
  # the query (e.g. { mode: :secondary }).
172
- # @option opts [ Boolean | nil ] multi_collection Whether the query
172
+ # @option opts [ Boolean | nil ] :multi_collection Whether the query
173
173
  # results could potentially come from multiple collections. When true,
174
174
  # these results will be stored under the nil namespace key and cleared
175
175
  # on every write command.
@@ -0,0 +1,255 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2016-2023 MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Mongo
18
+ class Server
19
+ class AppMetadata
20
+ # Implements the logic from the handshake spec, for deducing and
21
+ # reporting the current FaaS environment in which the program is
22
+ # executing.
23
+ #
24
+ # @api private
25
+ class Environment
26
+ # Error class for reporting that too many discriminators were found
27
+ # in the environment. (E.g. if the environment reports that it is
28
+ # running under both AWS and Azure.)
29
+ class TooManyEnvironments < Mongo::Error; end
30
+
31
+ # Error class for reporting that a required environment variable is
32
+ # missing.
33
+ class MissingVariable < Mongo::Error; end
34
+
35
+ # Error class for reporting that the wrong type was given for a
36
+ # field.
37
+ class TypeMismatch < Mongo::Error; end
38
+
39
+ # Error class for reporting that the value for a field is too long.
40
+ class ValueTooLong < Mongo::Error; end
41
+
42
+ # This value is not explicitly specified in the spec, only implied to be
43
+ # less than 512.
44
+ MAXIMUM_VALUE_LENGTH = 500
45
+
46
+ # The mapping that determines which FaaS environment is active, based
47
+ # on which environment variable(s) are present.
48
+ DISCRIMINATORS = {
49
+ 'AWS_EXECUTION_ENV' => { pattern: /^AWS_Lambda_/, name: 'aws.lambda' },
50
+ 'AWS_LAMBDA_RUNTIME_API' => { name: 'aws.lambda' },
51
+ 'FUNCTIONS_WORKER_RUNTIME' => { name: 'azure.func' },
52
+ 'K_SERVICE' => { name: 'gcp.func' },
53
+ 'FUNCTION_NAME' => { name: 'gcp.func' },
54
+ 'VERCEL' => { name: 'vercel' },
55
+ }.freeze
56
+
57
+ # Describes how to coerce values of the specified type.
58
+ COERCIONS = {
59
+ string: ->(v) { String(v) },
60
+ integer: ->(v) { Integer(v) }
61
+ }.freeze
62
+
63
+ # Describes which fields are required for each FaaS environment,
64
+ # along with their expected types, and how they should be named in
65
+ # the handshake document.
66
+ FIELDS = {
67
+ 'aws.lambda' => {
68
+ 'AWS_REGION' => { field: :region, type: :string },
69
+ 'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => { field: :memory_mb, type: :integer },
70
+ },
71
+
72
+ 'azure.func' => {},
73
+
74
+ 'gcp.func' => {
75
+ 'FUNCTION_MEMORY_MB' => { field: :memory_mb, type: :integer },
76
+ 'FUNCTION_TIMEOUT_SEC' => { field: :timeout_sec, type: :integer },
77
+ 'FUNCTION_REGION' => { field: :region, type: :string },
78
+ },
79
+
80
+ 'vercel' => {
81
+ 'VERCEL_URL' => { field: :url, type: :string },
82
+ 'VERCEL_REGION' => { field: :region, type: :string },
83
+ },
84
+ }.freeze
85
+
86
+ # @return [ String | nil ] the name of the FaaS environment that was
87
+ # detected, or nil if no valid FaaS environment was detected.
88
+ attr_reader :name
89
+
90
+ # @return [ Hash | nil ] the fields describing the detected FaaS
91
+ # environment.
92
+ attr_reader :fields
93
+
94
+ # @return [ String | nil ] the error message explaining why a valid
95
+ # FaaS environment was not detected, or nil if no error occurred.
96
+ #
97
+ # @note These error messagess are not to be propogated to the
98
+ # user; they are intended only for troubleshooting and debugging.)
99
+ attr_reader :error
100
+
101
+ # Create a new AppMetadata::Environment object, initializing it from
102
+ # the current ENV variables. If no FaaS environment is detected, or
103
+ # if the environment contains invalid or contradictory state, it will
104
+ # be initialized with {{name}} set to {{nil}}.
105
+ def initialize
106
+ @error = nil
107
+ @name = detect_environment
108
+ populate_fields
109
+ rescue TooManyEnvironments => e
110
+ self.error = "too many environments detected: #{e.message}"
111
+ rescue MissingVariable => e
112
+ self.error = "missing environment variable: #{e.message}"
113
+ rescue TypeMismatch => e
114
+ self.error = e.message
115
+ rescue ValueTooLong => e
116
+ self.error = "value for #{e.message} is too long"
117
+ end
118
+
119
+ # Queries whether the current environment is a valid FaaS environment.
120
+ #
121
+ # @return [ true | false ] whether the environment is a FaaS
122
+ # environment or not.
123
+ def faas?
124
+ @name != nil
125
+ end
126
+
127
+ # Queries whether the current environment is a valid AWS Lambda
128
+ # environment.
129
+ #
130
+ # @return [ true | false ] whether the environment is a AWS Lambda
131
+ # environment or not.
132
+ def aws?
133
+ @name == 'aws.lambda'
134
+ end
135
+
136
+ # Queries whether the current environment is a valid Azure
137
+ # environment.
138
+ #
139
+ # @return [ true | false ] whether the environment is a Azure
140
+ # environment or not.
141
+ def azure?
142
+ @name == 'azure.func'
143
+ end
144
+
145
+ # Queries whether the current environment is a valid GCP
146
+ # environment.
147
+ #
148
+ # @return [ true | false ] whether the environment is a GCP
149
+ # environment or not.
150
+ def gcp?
151
+ @name == 'gcp.func'
152
+ end
153
+
154
+ # Queries whether the current environment is a valid Vercel
155
+ # environment.
156
+ #
157
+ # @return [ true | false ] whether the environment is a Vercel
158
+ # environment or not.
159
+ def vercel?
160
+ @name == 'vercel'
161
+ end
162
+
163
+ # Compiles the detected environment information into a Hash. It will
164
+ # always include a {{name}} key, but may include other keys as well,
165
+ # depending on the detected FaaS environment. (See the handshake
166
+ # spec for details.)
167
+ #
168
+ # @return [ Hash ] the detected environment information.
169
+ def to_h
170
+ fields.merge(name: name)
171
+ end
172
+
173
+ private
174
+
175
+ # Searches the DESCRIMINATORS list to see which (if any) apply to
176
+ # the current environment.
177
+ #
178
+ # @return [ String | nil ] the name of the detected FaaS provider.
179
+ #
180
+ # @raise [ TooManyEnvironments ] if the environment contains
181
+ # discriminating variables for more than one FaaS provider.
182
+ def detect_environment
183
+ matches = DISCRIMINATORS.keys.select { |k| discriminator_matches?(k) }
184
+ names = matches.map { |m| DISCRIMINATORS[m][:name] }.uniq
185
+
186
+ raise TooManyEnvironments, names.join(', ') if names.length > 1
187
+
188
+ names.first
189
+ end
190
+
191
+ # Determines whether the named environment variable exists, and (if
192
+ # a pattern has been declared for that descriminator) whether the
193
+ # pattern matches the value of the variable.
194
+ #
195
+ # @param [ String ] var the name of the environment variable
196
+ #
197
+ # @return [ true | false ] if the variable describes the current
198
+ # environment or not.
199
+ def discriminator_matches?(var)
200
+ return false unless ENV[var]
201
+
202
+ disc = DISCRIMINATORS[var]
203
+ return true unless disc[:pattern]
204
+
205
+ disc[:pattern].match?(ENV[var])
206
+ end
207
+
208
+ # Extracts environment information from the current environment
209
+ # variables, based on the detected FaaS environment. Populates the
210
+ # {{@fields}} instance variable.
211
+ def populate_fields
212
+ return unless name
213
+
214
+ @fields = FIELDS[name].each_with_object({}) do |(var, defn), fields|
215
+ fields[defn[:field]] = extract_field(var, defn)
216
+ end
217
+ end
218
+
219
+ # Extracts the named variable from the environment and validates it
220
+ # against its declared definition.
221
+ #
222
+ # @param [ String ] var The name of the environment variable to look
223
+ # for.
224
+ # @param [ Hash ] definition The definition of the field that applies
225
+ # to the named variable.
226
+ #
227
+ # @return [ Integer | String ] the validated and coerced value of the
228
+ # given environment variable.
229
+ #
230
+ # @raise [ MissingVariable ] if the environment does not include a
231
+ # variable required by the current FaaS provider.
232
+ # @raise [ ValueTooLong ] if a required variable is too long.
233
+ # @raise [ TypeMismatch ] if a required variable cannot be coerced to
234
+ # the expected type.
235
+ def extract_field(var, definition)
236
+ raise MissingVariable, var unless ENV[var]
237
+ raise ValueTooLong, var if ENV[var].length > MAXIMUM_VALUE_LENGTH
238
+
239
+ COERCIONS[definition[:type]].call(ENV[var])
240
+ rescue ArgumentError
241
+ raise TypeMismatch,
242
+ "#{var} must be #{definition[:type]} (got #{ENV[var].inspect})"
243
+ end
244
+
245
+ # Sets the error message to the given value and sets the name to nil.
246
+ #
247
+ # @param [ String ] msg The error message to store.
248
+ def error=(msg)
249
+ @name = nil
250
+ @error = msg
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2016-2023 MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Mongo
18
+ class Server
19
+ class AppMetadata
20
+ # Implements the metadata truncation logic described in the handshake
21
+ # spec.
22
+ #
23
+ # @api private
24
+ class Truncator
25
+ # @return [ BSON::Document ] the document being truncated.
26
+ attr_reader :document
27
+
28
+ # The max application metadata document byte size.
29
+ MAX_DOCUMENT_SIZE = 512
30
+
31
+ # Creates a new Truncator instance and tries enforcing the maximum
32
+ # document size on the given document.
33
+ #
34
+ # @param [ BSON::Document] document The document to (potentially)
35
+ # truncate.
36
+ #
37
+ # @note The document is modified in-place; if you wish to keep the
38
+ # original unchanged, you must deep-clone it prior to sending it to
39
+ # a truncator.
40
+ def initialize(document)
41
+ @document = document
42
+ try_truncate!
43
+ end
44
+
45
+ # The current size of the document, in bytes, as a serialized BSON
46
+ # document.
47
+ #
48
+ # @return [ Integer ] the size of the document
49
+ def size
50
+ @document.to_bson.to_s.length
51
+ end
52
+
53
+ # Whether the document fits within the required maximum document size.
54
+ #
55
+ # @return [ true | false ] if the document is okay or not.
56
+ def ok?
57
+ size <= MAX_DOCUMENT_SIZE
58
+ end
59
+
60
+ private
61
+
62
+ # How many extra bytes must be trimmed before the document may be
63
+ # considered #ok?.
64
+ #
65
+ # @return [ Integer ] how many bytes larger the document is than the
66
+ # maximum document size.
67
+ def excess
68
+ size - MAX_DOCUMENT_SIZE
69
+ end
70
+
71
+ # Attempt to truncate the document using the documented metadata
72
+ # priorities (see the handshake specification).
73
+ def try_truncate!
74
+ %i[ env_fields os_fields env platform ].each do |target|
75
+ break if ok?
76
+
77
+ send(:"try_truncate_#{target}!")
78
+ end
79
+ end
80
+
81
+ # Attempt to truncate or remove the {{:platform}} key from the
82
+ # document.
83
+ def try_truncate_platform!
84
+ @document.delete(:platform) unless try_truncate_string(@document[:platform])
85
+ end
86
+
87
+ # Attempt to truncate the keys in the {{:env}} subdocument.
88
+ def try_truncate_env_fields!
89
+ try_truncate_hash(@document[:env], reserved: %w[ name ])
90
+ end
91
+
92
+ # Attempt to truncate the keys in the {{:os}} subdocument.
93
+ def try_truncate_os_fields!
94
+ try_truncate_hash(@document[:os], reserved: %w[ type ])
95
+ end
96
+
97
+ # Remove the {{:env}} key from the document.
98
+ def try_truncate_env!
99
+ @document.delete(:env)
100
+ end
101
+
102
+ # A helper method for truncating a string (in-place) by whatever
103
+ # {{#excess}} is required.
104
+ #
105
+ # @param [ String ] string the string value to truncate.
106
+ #
107
+ # @note the parameter is modified in-place.
108
+ def try_truncate_string(string)
109
+ length = string&.length || 0
110
+
111
+ return false if excess > length
112
+
113
+ string[(length - excess)..-1] = ''
114
+ end
115
+
116
+ # A helper method for removing the keys of a Hash (in-place) until
117
+ # the document is the necessary size. The keys are considered in order
118
+ # (using the Hash's native key ordering), and each will be removed from
119
+ # the hash in turn, until the document is the necessary size.
120
+ #
121
+ # Any keys in the {{reserved}} list will be ignored.
122
+ #
123
+ # @param [ Hash | nil ] hash the Hash instance to consider.
124
+ # @param [ Array ] reserved the list of keys to ignore in the hash.
125
+ #
126
+ # @note the hash parameter is modified in-place.
127
+ def try_truncate_hash(hash, reserved: [])
128
+ return false unless hash
129
+
130
+ keys = hash.keys - reserved
131
+ keys.each do |key|
132
+ hash.delete(key)
133
+
134
+ return true if ok?
135
+ end
136
+
137
+ false
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end