neo4j-ruby-driver 6.0.3.alpha.0-java → 6.2.1.beta.2-java

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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +113 -411
  3. data/lib/neo4j/driver/auth_token_manager.rb +29 -0
  4. data/lib/neo4j/driver/client_certificate_manager.rb +31 -0
  5. data/lib/neo4j/driver/exceptions/neo4j_exception.rb +16 -7
  6. data/lib/neo4j/driver/exceptions/no_such_record_exception.rb +1 -1
  7. data/lib/neo4j/driver/exceptions/protocol_exception.rb +0 -5
  8. data/lib/neo4j/driver/exceptions/security_retryable_exception.rb +14 -0
  9. data/lib/neo4j/driver/exceptions/transaction_terminated_exception.rb +13 -0
  10. data/lib/neo4j/driver/ext/auth_token.rb +17 -0
  11. data/{jruby → lib}/neo4j/driver/ext/auth_tokens.rb +5 -1
  12. data/lib/neo4j/driver/ext/bookmark_managers.rb +21 -0
  13. data/lib/neo4j/driver/ext/client_certificates.rb +21 -0
  14. data/{jruby → lib}/neo4j/driver/ext/config_converter.rb +36 -7
  15. data/{jruby → lib}/neo4j/driver/ext/exception_checkable.rb +3 -3
  16. data/lib/neo4j/driver/ext/exception_mapper.rb +173 -0
  17. data/lib/neo4j/driver/ext/graph_database.rb +25 -0
  18. data/lib/neo4j/driver/ext/internal/clock_adapter.rb +33 -0
  19. data/lib/neo4j/driver/ext/internal/cluster/routing_table_registry_impl.rb +28 -0
  20. data/lib/neo4j/driver/ext/internal/driver_factory.rb +54 -0
  21. data/lib/neo4j/driver/ext/internal/internal_notification_severity.rb +15 -0
  22. data/lib/neo4j/driver/ext/internal/reflection.rb +22 -0
  23. data/lib/neo4j/driver/ext/internal/summary/internal_gql_notification.rb +22 -0
  24. data/lib/neo4j/driver/ext/internal/summary/internal_gql_status_object.rb +18 -0
  25. data/{jruby → lib}/neo4j/driver/ext/internal/summary/internal_plan.rb +3 -5
  26. data/lib/neo4j/driver/ext/internal/summary/internal_query_profile.rb +36 -0
  27. data/lib/neo4j/driver/ext/internal/summary/internal_result_summary.rb +44 -0
  28. data/lib/neo4j/driver/ext/internal_driver.rb +102 -0
  29. data/{jruby → lib}/neo4j/driver/ext/internal_result.rb +2 -2
  30. data/{jruby → lib}/neo4j/driver/ext/internal_session.rb +5 -1
  31. data/lib/neo4j/driver/ext/logger.rb +55 -0
  32. data/lib/neo4j/driver/ext/map_converter.rb +13 -0
  33. data/lib/neo4j/driver/ext/neo_converter.rb +70 -0
  34. data/lib/neo4j/driver/ext/plain_map_converter.rb +11 -0
  35. data/{jruby → lib}/neo4j/driver/ext/ruby_converter.rb +20 -6
  36. data/lib/neo4j/driver/ext/unsupported_type.rb +14 -0
  37. data/lib/neo4j/driver/internal/deprecator.rb +9 -5
  38. data/lib/neo4j/driver/internal/duration_normalizer.rb +14 -39
  39. data/lib/neo4j/driver/internal/internal_auth_token_manager.rb +37 -0
  40. data/lib/neo4j/driver/internal/validator.rb +16 -2
  41. data/lib/neo4j/driver/summary/query_type.rb +5 -1
  42. data/lib/neo4j/driver/types/duration.rb +76 -0
  43. data/lib/neo4j/driver/types/local_date_time.rb +77 -8
  44. data/lib/neo4j/driver/types/local_time.rb +50 -7
  45. data/lib/neo4j/driver/types/offset_time.rb +78 -7
  46. data/lib/neo4j/driver/types/point.rb +38 -18
  47. data/lib/neo4j/driver/types/temporal_value.rb +44 -0
  48. data/lib/neo4j/driver/types/unresolvable_zoned_date_time.rb +38 -0
  49. data/{jruby → lib}/neo4j/driver/version.rb +1 -1
  50. data/{jruby → lib}/neo4j/driver.rb +31 -7
  51. data/lib/neo4j-ruby-driver_jars.rb +14 -0
  52. data/lib/neo4j-ruby-driver_loader.rb +34 -20
  53. data/lib/org/neo4j/driver/neo4j-java-driver-all/6.2.1/neo4j-java-driver-all-6.2.1.jar +0 -0
  54. data/lib/org/neo4j/driver/neo4j-java-driver-observation-metrics/6.2.1/neo4j-java-driver-observation-metrics-6.2.1.jar +0 -0
  55. data/lib/org/reactivestreams/reactive-streams/1.0.4/reactive-streams-1.0.4.jar +0 -0
  56. metadata +76 -226
  57. data/jruby/neo4j/driver/ext/async_converter.rb +0 -55
  58. data/jruby/neo4j/driver/ext/exception_mapper.rb +0 -92
  59. data/jruby/neo4j/driver/ext/graph_database.rb +0 -33
  60. data/jruby/neo4j/driver/ext/internal/async/internal_async_session.rb +0 -23
  61. data/jruby/neo4j/driver/ext/internal/cluster/routing_table_registry_impl.rb +0 -15
  62. data/jruby/neo4j/driver/ext/internal/cursor/disposable_async_result_cursor.rb +0 -15
  63. data/jruby/neo4j/driver/ext/internal/driver_factory.rb +0 -24
  64. data/jruby/neo4j/driver/ext/internal/summary/internal_result_summary.rb +0 -34
  65. data/jruby/neo4j/driver/ext/internal_driver.rb +0 -52
  66. data/jruby/neo4j/driver/ext/logger.rb +0 -60
  67. data/jruby/neo4j/driver/ext/map_converter.rb +0 -14
  68. data/jruby/neo4j/driver/ext/neo_converter.rb +0 -59
  69. data/lib/neo4j/driver/internal/bolt_server_address.rb +0 -97
  70. data/lib/neo4j/driver/synchronizable.rb +0 -23
  71. data/lib/neo4j/driver/types/time.rb +0 -45
  72. /data/{jruby → lib}/neo4j/driver/ext/internal/abstract_query_runner.rb +0 -0
  73. /data/{jruby → lib}/neo4j/driver/ext/internal/eager_result_value.rb +0 -0
  74. /data/{jruby → lib}/neo4j/driver/ext/internal/metrics/internal_connection_pool_metrics.rb +0 -0
  75. /data/{jruby → lib}/neo4j/driver/ext/internal/summary/internal_notification.rb +0 -0
  76. /data/{jruby → lib}/neo4j/driver/ext/internal_entity.rb +0 -0
  77. /data/{jruby → lib}/neo4j/driver/ext/internal_keys.rb +0 -0
  78. /data/{jruby → lib}/neo4j/driver/ext/internal_node.rb +0 -0
  79. /data/{jruby → lib}/neo4j/driver/ext/internal_record.rb +0 -0
  80. /data/{jruby → lib}/neo4j/driver/ext/internal_relationship.rb +0 -0
  81. /data/{jruby → lib}/neo4j/driver/ext/internal_transaction.rb +0 -0
  82. /data/{jruby → lib}/neo4j/driver/ext/query.rb +0 -0
  83. /data/{jruby → lib}/neo4j/driver/ext/run_override.rb +0 -0
  84. /data/{jruby → lib}/neo4j/driver/ext/start_end_naming.rb +0 -0
@@ -4,17 +4,26 @@ module Neo4j
4
4
  module Driver
5
5
  module Exceptions
6
6
  class Neo4jException < RuntimeError
7
- attr_reader :code, :suppressed
7
+ attr_reader :code, :suppressed, :gql_status, :status_description,
8
+ :classification, :raw_classification, :diagnostic_record, :gql_cause
8
9
 
9
- def initialize(*args)
10
- args, exceptions = args.partition { |arg| arg.is_a?(String) }
11
- super(args.pop)
12
- @code = args.pop
13
- add_suppressed(*exceptions)
10
+ def initialize(message = nil, code: nil, suppressed: nil,
11
+ gql_status: nil, status_description: nil,
12
+ classification: nil, raw_classification: nil,
13
+ diagnostic_record: nil, gql_cause: nil)
14
+ super(message)
15
+ @code = code
16
+ @suppressed = Array(suppressed)
17
+ @gql_status = gql_status
18
+ @status_description = status_description
19
+ @classification = classification
20
+ @raw_classification = raw_classification
21
+ @diagnostic_record = diagnostic_record
22
+ @gql_cause = gql_cause
14
23
  end
15
24
 
16
25
  def add_suppressed(*exceptions)
17
- (@suppressed ||= []).concat(exceptions) if exceptions.any?
26
+ @suppressed.concat(exceptions)
18
27
  end
19
28
  end
20
29
  end
@@ -6,7 +6,7 @@ module Neo4j
6
6
  class NoSuchRecordException < RuntimeError
7
7
  EMPTY = 'Cannot retrieve a single record, because this result is empty.'
8
8
  TOO_MANY = 'Expected a result with a single record, but this result ' \
9
- 'contains at least one more. Ensure your query returns only one record.'
9
+ 'contains at least one more. Ensure your query returns only one record.'
10
10
  NO_MORE = 'No more records'
11
11
  NO_PEEK_PAST = 'Cannot peek past the last record'
12
12
 
@@ -6,11 +6,6 @@ module Neo4j
6
6
  # A signal that the contract for client-server communication has broken down.
7
7
  # The user should contact support and cannot resolve this his or herself.
8
8
  class ProtocolException < Neo4jException
9
- CODE = "Protocol violation: "
10
-
11
- def initialize(message, e = nil)
12
- super(nil, "#{CODE}#{message}", e)
13
- end
14
9
  end
15
10
  end
16
11
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Exceptions
6
+ # A security exception the driver considers retryable: the auth-token
7
+ # manager handled the underlying security failure (e.g. an expired
8
+ # token), so a managed transaction may retry with a fresh token.
9
+ # Mirrors org.neo4j.driver.exceptions.SecurityRetryableException.
10
+ class SecurityRetryableException < SecurityException
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Exceptions
6
+ # The transaction has been terminated, so the work it was running
7
+ # cannot continue. Mirrors
8
+ # org.neo4j.driver.exceptions.TransactionTerminatedException.
9
+ class TransactionTerminatedException < ClientException
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ # Adds a Ruby-friendly `to_h` to Java's `AuthToken`. The MRI
7
+ # driver's `AuthTokens.basic` etc. already return Hashes (so
8
+ # `.to_h` is the Hash identity); this mirror lets impl-agnostic
9
+ # callers — e.g. testkit-backend's `serialize_auth_token` —
10
+ # consume an AuthToken without poking at Java's `toMap` /
11
+ # `Value#asString`.
12
+ module AuthToken
13
+ def to_h = to_map.as_ruby_object
14
+ end
15
+ end
16
+ end
17
+ end
@@ -11,7 +11,11 @@ module Neo4j
11
11
  super
12
12
  end
13
13
 
14
- def custom(principal, credentials, realm, scheme, **parameters)
14
+ # `parameters` is a positional Hash (matching Java's Map arg and
15
+ # the MRI flavour), so the same call works on both impls — a
16
+ # `**`-kwargs signature here would reject the positional-hash
17
+ # calls every caller uses (testkit AuthorizationToken, specs).
18
+ def custom(principal, credentials, realm, scheme, parameters = {})
15
19
  super(principal, credentials, realm, scheme, to_neo(parameters))
16
20
  end
17
21
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ # Mirrors Java's `BookmarkManagers.defaultManager(BookmarkManagerConfig)`
7
+ # factory (cf. Ext::AuthTokens / Ext::GraphDatabase), but with a Ruby
8
+ # keyword-arg surface instead of a config builder. The supplier/consumer
9
+ # callbacks are plain Procs — JRuby coerces them to the Java
10
+ # Supplier<Set<Bookmark>> / Consumer<Set<Bookmark>> functional interfaces.
11
+ module BookmarkManagers
12
+ include ConfigConverter
13
+
14
+ def default_manager(initial_bookmarks: nil, bookmarks_supplier: nil, bookmarks_consumer: nil)
15
+ super(to_java_config(Neo4j::Driver::BookmarkManagerConfig, initial_bookmarks:, bookmarks_supplier:,
16
+ bookmarks_consumer:))
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ # Ruby-friendly surface over org.neo4j.driver.ClientCertificates,
7
+ # prepended onto its singleton class (cf. Ext::GraphDatabase /
8
+ # Ext::BookmarkManagers) rather than shadowing the Java class. Lets the
9
+ # flavour-agnostic testkit backend call
10
+ # ClientCertificates.of(certpath, keypath[, password]) with path strings;
11
+ # the Java factory takes java.io.File, so wrap and delegate via super.
12
+ module ClientCertificates
13
+ def of(certfile, keyfile, password = nil)
14
+ cert = java.io.File.new(certfile.to_s)
15
+ key = java.io.File.new(keyfile.to_s)
16
+ password ? super(cert, key, password) : super(cert, key)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -5,6 +5,7 @@ module Neo4j
5
5
  module Ext
6
6
  module ConfigConverter
7
7
  include NeoConverter
8
+ include Driver::Internal::DurationNormalizer
8
9
 
9
10
  private
10
11
 
@@ -13,29 +14,54 @@ module Neo4j
13
14
  end
14
15
 
15
16
  def apply_to(builder, **hash)
16
- hash.compact.reduce(builder) { |object, (key, value)| object.send(*config_method(key, value)) }
17
+ # nil means "unset" (dropped) for every key except :bookmark_manager,
18
+ # where nil is meaningful — it disables the manager via
19
+ # with_bookmark_manager(null), distinct from leaving it at the default.
20
+ hash.reject { |key, value| value.nil? && key != :bookmark_manager }
21
+ .reduce(builder) { |object, (key, value)| object.send(*config_method(key, value)) }
17
22
  end
18
23
 
19
24
  def config_method(key, value)
20
25
  method = :"with_#{key}"
21
26
  unit = nil
22
27
  case key.to_s
23
- when 'encryption', 'driver_metrics', 'hostname_verification'
28
+ when 'encryption', 'hostname_verification'
24
29
  method = :"without_#{key}" unless value
25
30
  value = nil
26
31
  when 'timeout'
27
- value = java.time.Duration.ofMillis(Driver::Internal::DurationNormalizer.milliseconds(value))
32
+ value = java.time.Duration.ofMillis(timeout_to_milliseconds(value))
28
33
  when /time(out)?$/, 'routing_table_purge_delay'
29
- value = Driver::Internal::DurationNormalizer.milliseconds(value) || -1
34
+ value = timeout_to_milliseconds(value) || -1
30
35
  unit = java.util.concurrent.TimeUnit::MILLISECONDS
31
36
  when 'logger'
32
37
  method = :with_logging
33
38
  value = Neo4j::Driver::Ext::Logger.new(value)
34
39
  when 'resolver'
40
+ # LinkedHashSet (not HashSet) preserves the order the caller's
41
+ # resolver returned. The Java driver's ServerAddressResolver
42
+ # contract is `Set<ServerAddress>`, but its routing layer
43
+ # iterates that Set in its natural order to pick a router to
44
+ # probe — with a plain HashSet that's hash-of-host:port order,
45
+ # so the driver effectively picks at random and tests like
46
+ # test_should_read_successfully_on_empty_discovery_result_using_session_run
47
+ # (which expect probes in the resolver-returned order) become
48
+ # flaky.
35
49
  proc = value
36
- value = ->(address) { java.util.HashSet.new(proc.call(address)) }
50
+ value = ->(address) { to_java_set(proc.call(address), java.util.LinkedHashSet) }
51
+ when 'bookmarks_supplier'
52
+ proc = value
53
+ value = -> { to_java_set(proc.call) }
54
+ when 'bookmarks_consumer'
55
+ proc = value
56
+ value = ->(bookmarks) { proc.call(Set.new(bookmarks)) }
37
57
  when 'bookmarks'
38
58
  return [method, *value]
59
+ when 'initial_bookmarks'
60
+ value = to_java_set(value)
61
+ when 'bookmark_manager'
62
+ # A BookmarkManager sets it; nil disables it (with_bookmark_manager(null)).
63
+ # `return` skips the trailing `.compact` so a nil arg survives to the builder.
64
+ return [method, value]
39
65
  when 'trust_strategy'
40
66
  value = trust_strategy(**value)
41
67
  when 'revocation_strategy'
@@ -59,7 +85,7 @@ module Neo4j
59
85
  case strategy
60
86
  when :trust_custom_certificates
61
87
  Config::TrustStrategy
62
- .trust_custom_certificate_signed_by(*config.delete(:cert_files).map(&java.io.File.method(:new)))
88
+ .trust_custom_certificate_signed_by(*config.delete(:cert_files).map(&java.io.File.method(:new)))
63
89
  else
64
90
  Config::TrustStrategy.send(strategy)
65
91
  end
@@ -71,12 +97,15 @@ module Neo4j
71
97
  value_of(org.neo4j.driver.internal.InternalNotificationSeverity, minimum_severity).or_else(nil),
72
98
  disabled_categories
73
99
  &.map { |value| value_of(org.neo4j.driver.NotificationClassification, value) }
74
- &.then(&java.util.HashSet.method(:new)))
100
+ &.then(&method(:to_java_set))
101
+ )
75
102
  end
76
103
 
77
104
  def value_of(klass, value)
78
105
  klass.value_of(value&.to_s&.upcase)
79
106
  end
107
+
108
+ def to_java_set(enumerable, class_name = java.util.HashSet) = class_name.new(enumerable.to_a)
80
109
  end
81
110
  end
82
111
  end
@@ -17,9 +17,9 @@ module Neo4j
17
17
  rescue Neo4j::Driver::Exceptions::ServiceUnavailableException => e
18
18
  raise(throwable(e.cause) || Java::OrgNeo4jDriverExceptions::ServiceUnavailableException.new(e.message))
19
19
  rescue Neo4j::Driver::Exceptions::Neo4jException,
20
- Neo4j::Driver::Exceptions::NoSuchRecordException,
21
- Neo4j::Driver::Exceptions::UntrustedServerException,
22
- Neo4j::Driver::Exceptions::IllegalStateException => e
20
+ Neo4j::Driver::Exceptions::NoSuchRecordException,
21
+ Neo4j::Driver::Exceptions::UntrustedServerException,
22
+ Neo4j::Driver::Exceptions::IllegalStateException => e
23
23
  raise(throwable(e.cause) || e)
24
24
  end
25
25
 
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module ExceptionMapper
7
+ java_import org.neo4j.driver.exceptions.AuthenticationException
8
+ java_import org.neo4j.driver.exceptions.AuthorizationExpiredException
9
+ java_import org.neo4j.driver.exceptions.ClientException
10
+ java_import org.neo4j.driver.exceptions.ConnectionReadTimeoutException
11
+ java_import org.neo4j.driver.exceptions.DatabaseException
12
+ java_import org.neo4j.driver.exceptions.DiscoveryException
13
+ java_import org.neo4j.driver.exceptions.FatalDiscoveryException
14
+ java_import org.neo4j.driver.exceptions.Neo4jException
15
+ java_import org.neo4j.driver.exceptions.ProtocolException
16
+ java_import org.neo4j.driver.exceptions.ResultConsumedException
17
+ java_import org.neo4j.driver.exceptions.SecurityException
18
+ java_import org.neo4j.driver.exceptions.SecurityRetryableException
19
+ java_import org.neo4j.driver.exceptions.ServiceUnavailableException
20
+ java_import org.neo4j.driver.exceptions.SessionExpiredException
21
+ java_import org.neo4j.driver.exceptions.TokenExpiredException
22
+ java_import org.neo4j.driver.exceptions.TransactionNestingException
23
+ java_import org.neo4j.driver.exceptions.TransactionTerminatedException
24
+ java_import org.neo4j.driver.exceptions.TransientException
25
+ java_import org.neo4j.driver.exceptions.UnsupportedFeatureException
26
+ java_import org.neo4j.driver.exceptions.UntrustedServerException
27
+
28
+ def mapped_exception(exception)
29
+ mapped_neo4j_exception_class(exception)
30
+ &.new(exception.message, **neo4j_exception_kwargs(exception)) ||
31
+ mapped_runtime_exception_class(exception)&.new(exception.message) || exception
32
+ end
33
+
34
+ # Like mapped_exception, but chains the original Java exception as
35
+ # `.cause`. At a `raise mapped_exception(e)` site inside a
36
+ # `rescue => e` block Ruby sets `.cause` to the Java exception
37
+ # automatically (which reverse_check round-trips on via
38
+ # `throwable(e.cause)`). At sites that hand the mapped exception
39
+ # off as a *value* (AuthTokenManager#handle_security_exception)
40
+ # there is no such auto-set, so do it explicitly here. Ruby has no
41
+ # `cause=` setter;
42
+ # `raise … cause:` is the only way to attach one.
43
+ def mapped_exception_with_cause(exception)
44
+ mapped = mapped_exception(exception)
45
+ return mapped if mapped.equal?(exception) # unmapped passthrough — no self-cause
46
+
47
+ raise mapped, cause: exception
48
+ rescue StandardError => e
49
+ # Only the mapped exception we just raised becomes a return
50
+ # value; anything else (e.g. a bug in mapped_exception) must
51
+ # propagate rather than be silently turned into a value.
52
+ raise unless e.equal?(mapped)
53
+
54
+ e
55
+ end
56
+
57
+ private
58
+
59
+ def suppressed(e)
60
+ e.suppressed.map(&method(:mapped_exception))
61
+ end
62
+
63
+ # Pull through the GQL fields the testkit DriverError schema
64
+ # asserts on (gql_status, status_description, classification,
65
+ # raw_classification, diagnostic_record). All of these were
66
+ # added to org.neo4j.driver.exceptions.Neo4jException in the
67
+ # GQL-aware Bolt 5.7+ work; auto-aliased on JRuby.
68
+ #
69
+ # Java's accessors return Optional<T> for the nullable fields;
70
+ # unwrap to nil/value here so the Ruby exception only ever
71
+ # exposes plain Ruby values — the JRuby driver isn't allowed to
72
+ # leak Java types over the public API.
73
+ def neo4j_exception_kwargs(e)
74
+ {
75
+ code: effective_code(e),
76
+ suppressed: suppressed(e),
77
+ gql_status: unwrap_optional(e.gql_status),
78
+ status_description: unwrap_optional(e.status_description),
79
+ classification: unwrap_optional(e.classification)&.to_s,
80
+ raw_classification: unwrap_optional(e.raw_classification),
81
+ diagnostic_record: unwrap_optional(e.diagnostic_record)&.to_h,
82
+ gql_cause: gql_cause(e)
83
+ }
84
+ end
85
+
86
+ # The GQL cause chain (Neo4jException#gqlCause), mapped recursively so
87
+ # the Ruby exception carries the nested cause the testkit error shape
88
+ # asserts on. Each cause is itself a Neo4jException, so mapped_exception
89
+ # recurses (its own gqlCause included) until the chain ends.
90
+ def gql_cause(e)
91
+ cause = unwrap_optional(e.gql_cause)
92
+ cause && mapped_exception(cause)
93
+ end
94
+
95
+ def unwrap_optional(v)
96
+ v.respond_to?(:or_else) ? v.or_else(nil) : v
97
+ end
98
+
99
+ # A driver-generated wrapper — e.g. the SessionExpiredException raised
100
+ # for a write against a read-only database — reports code "N/A" but
101
+ # chains the original failure (the shaded BoltFailureException, which
102
+ # carries the real Neo4j code) as its cause. testkit asserts on that
103
+ # original code, so walk the cause chain to the first real code — or
104
+ # nil when none carries one ('N/A' is Java's no-code sentinel, which
105
+ # the Ruby API represents as nil, the same as MRI).
106
+ def effective_code(e)
107
+ return unless e
108
+
109
+ code = e.code if e.respond_to?(:code)
110
+ return code if code && code != 'N/A'
111
+
112
+ effective_code(e.cause)
113
+ end
114
+
115
+ def mapped_neo4j_exception_class(exception_class)
116
+ case exception_class
117
+ when AuthenticationException
118
+ Neo4j::Driver::Exceptions::AuthenticationException
119
+ when AuthorizationExpiredException
120
+ Neo4j::Driver::Exceptions::AuthorizationExpiredException
121
+ when FatalDiscoveryException
122
+ Neo4j::Driver::Exceptions::FatalDiscoveryException
123
+ when ResultConsumedException
124
+ Neo4j::Driver::Exceptions::ResultConsumedException
125
+ when TokenExpiredException
126
+ Neo4j::Driver::Exceptions::TokenExpiredException
127
+ when SecurityRetryableException # subclass of SecurityException — match first
128
+ Neo4j::Driver::Exceptions::SecurityRetryableException
129
+ when SecurityException
130
+ Neo4j::Driver::Exceptions::SecurityException
131
+ when TransactionNestingException
132
+ Neo4j::Driver::Exceptions::TransactionNestingException
133
+ when UnsupportedFeatureException
134
+ Neo4j::Driver::Exceptions::UnsupportedFeatureException
135
+ when TransactionTerminatedException # subclass of ClientException — match first
136
+ Neo4j::Driver::Exceptions::TransactionTerminatedException
137
+ when ClientException
138
+ Neo4j::Driver::Exceptions::ClientException
139
+ when ConnectionReadTimeoutException
140
+ Neo4j::Driver::Exceptions::ConnectionReadTimeoutException
141
+ when DatabaseException
142
+ Neo4j::Driver::Exceptions::DatabaseException
143
+ when DiscoveryException
144
+ Neo4j::Driver::Exceptions::DiscoveryException
145
+ when ProtocolException
146
+ Neo4j::Driver::Exceptions::ProtocolException
147
+ when ServiceUnavailableException
148
+ Neo4j::Driver::Exceptions::ServiceUnavailableException
149
+ when SessionExpiredException
150
+ Neo4j::Driver::Exceptions::SessionExpiredException
151
+ when TransientException
152
+ Neo4j::Driver::Exceptions::TransientException
153
+ when Neo4jException # base — catches plain GQL-cause exceptions (must be last)
154
+ Neo4j::Driver::Exceptions::Neo4jException
155
+ end
156
+ end
157
+
158
+ def mapped_runtime_exception_class(exception_class)
159
+ case exception_class
160
+ when Java::OrgNeo4jDriverExceptions::NoSuchRecordException
161
+ Neo4j::Driver::Exceptions::NoSuchRecordException
162
+ when Java::OrgNeo4jDriverExceptions::UntrustedServerException
163
+ Neo4j::Driver::Exceptions::UntrustedServerException
164
+ when Java::JavaLang::IllegalStateException
165
+ Neo4j::Driver::Exceptions::IllegalStateException
166
+ when Java::JavaLang::IllegalArgumentException
167
+ ArgumentError
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module GraphDatabase
7
+ extend AutoCloseable
8
+ include ConfigConverter
9
+ include ExceptionCheckable
10
+
11
+ auto_closeable :driver
12
+
13
+ def driver(uri, auth_token = nil, auth_token_manager: nil, **config)
14
+ check do
15
+ java_config = to_java_config(Neo4j::Driver::Config, **config)
16
+ auth_class = auth_token_manager ? org.neo4j.driver.AuthTokenManager : org.neo4j.driver.AuthToken
17
+ auth = auth_token_manager || auth_token || Driver::AuthTokens.none
18
+ java_method(:driver, [java.lang.String, auth_class, org.neo4j.driver.Config])
19
+ .call(uri.to_s, auth, java_config)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module Internal
7
+ # `java.time.Clock` adapter that delegates to any Ruby
8
+ # `clock` object responding to `#now_millis`. Used by
9
+ # `Internal::DriverFactory#to_clock` so testkit-backend's
10
+ # pure-Ruby `TestkitClock` can plug straight into Java's
11
+ # `DriverFactory#create_clock` seam.
12
+ class ClockAdapter < java.time.Clock
13
+ def initialize(ruby_clock)
14
+ super()
15
+ @ruby_clock = ruby_clock
16
+ end
17
+
18
+ def get_zone
19
+ raise java.lang.UnsupportedOperationException.new
20
+ end
21
+
22
+ def with_zone(_zone)
23
+ raise java.lang.UnsupportedOperationException.new
24
+ end
25
+
26
+ def instant
27
+ java.time.Instant.of_epoch_milli(@ruby_clock.now_millis)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j::Driver::Ext
4
+ module Internal
5
+ module Cluster
6
+ module RoutingTableRegistryImpl
7
+ def routing_table_handler(database)
8
+ if database
9
+ it = get_routing_table_handler(Neo4j::Driver::Internal::DatabaseName.database(database))
10
+ raise KeyError, "No routing table for database #{database.inspect}" unless it.present?
11
+
12
+ it.get
13
+ else
14
+ # The default/home database is keyed under the server-resolved
15
+ # name, not defaultDatabase(); in the single-table stub case
16
+ # (testkit's get_routing_table() with no database) return it.
17
+ handlers = Reflection.field(self, 'routingTableHandlers').values
18
+ unless handlers.size == 1
19
+ raise KeyError, "Cannot resolve default routing table (#{handlers.size} present; specify a database)"
20
+ end
21
+
22
+ handlers.first
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module Internal
7
+ # Ruby-side helpers prepended onto Java's
8
+ # `org.neo4j.driver.internal.DriverFactory` (aliased as
9
+ # `Neo4j::Driver::Internal::DriverFactory`). Lets further Ruby
10
+ # subclasses — testkit-backend's
11
+ # `DriverFactoryWithDomainNameResolver` — override the
12
+ # `protected` `getDomainNameResolver` / `create_clock` seams
13
+ # without naming any Java type: testkit hands its Ruby Proc /
14
+ # `TestkitClock` to the converter and gets back something the
15
+ # Java driver consumes (a `DomainNameResolver` SAM /
16
+ # `java.time.Clock`).
17
+ #
18
+ # `new_instance` is a Ruby-friendly wrapper over Java's 4-arg
19
+ # `newInstance` — takes a Ruby Hash config and an optional
20
+ # `ClientCertificateManager` (mutual TLS), and `super`s into the
21
+ # Java method.
22
+ module DriverFactory
23
+ include Ext::ConfigConverter
24
+ include Ext::ExceptionCheckable
25
+
26
+ def to_domain_name_resolver(resolver_proc)
27
+ return nil unless resolver_proc
28
+
29
+ lambda do |name|
30
+ resolver_proc.call(name).map do |addr|
31
+ java.net.InetAddress.get_by_name(addr)
32
+ rescue java.net.UnknownHostException => e
33
+ raise java.lang.RuntimeException.new(e)
34
+ end
35
+ end
36
+ end
37
+
38
+ def to_clock(clock)
39
+ ClockAdapter.new(clock)
40
+ end
41
+
42
+ def new_instance(uri, auth_token_manager, client_certificate_manager: nil, **config)
43
+ check do
44
+ super(java.net.URI.create(uri.to_s),
45
+ auth_token_manager,
46
+ client_certificate_manager,
47
+ to_java_config(Neo4j::Driver::Config, **config))
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module Internal
7
+ module InternalNotificationSeverity
8
+ extend Forwardable
9
+
10
+ delegate name: :type
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module Internal
7
+ # Read a private field off a Java object. Used to reach internal
8
+ # driver state the 6.x public API no longer exposes (the routing
9
+ # table registry chain consumed by testkit's GetRoutingTable).
10
+ module Reflection
11
+ def self.field(object, name)
12
+ object.java_class.declared_field(name).tap { |f| f.accessible = true }.get(object)
13
+ end
14
+
15
+ def self.field?(object, name)
16
+ object.java_class.declared_fields.any? { |f| f.name == name }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module Internal
7
+ module Summary
8
+ # A GqlStatusObject that is also a notification. Unwraps the Java
9
+ # Optionals so the backend sees plain Ruby (an InputPosition or nil,
10
+ # and parsed name strings) — mirroring the InternalNotification ext.
11
+ module InternalGqlNotification
12
+ def position = super.or_else(nil)
13
+ def classification = super.or_else(nil)&.name
14
+ def raw_classification = super.or_else(nil)
15
+ def severity = super.or_else(nil)&.name
16
+ def raw_severity = super.or_else(nil)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module Internal
7
+ module Summary
8
+ # A GqlStatusObject. Hydrate the Java Map<String, Value> diagnostic
9
+ # record to a plain (symbol-keyed) Ruby hash, like the rest of the
10
+ # summary; the GqlNotification subtype adds position/severity/…
11
+ module InternalGqlStatusObject
12
+ def diagnostic_record = super.as_ruby_object
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end