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.
- checksums.yaml +4 -4
- data/README.md +113 -411
- data/lib/neo4j/driver/auth_token_manager.rb +29 -0
- data/lib/neo4j/driver/client_certificate_manager.rb +31 -0
- data/lib/neo4j/driver/exceptions/neo4j_exception.rb +16 -7
- data/lib/neo4j/driver/exceptions/no_such_record_exception.rb +1 -1
- data/lib/neo4j/driver/exceptions/protocol_exception.rb +0 -5
- data/lib/neo4j/driver/exceptions/security_retryable_exception.rb +14 -0
- data/lib/neo4j/driver/exceptions/transaction_terminated_exception.rb +13 -0
- data/lib/neo4j/driver/ext/auth_token.rb +17 -0
- data/{jruby → lib}/neo4j/driver/ext/auth_tokens.rb +5 -1
- data/lib/neo4j/driver/ext/bookmark_managers.rb +21 -0
- data/lib/neo4j/driver/ext/client_certificates.rb +21 -0
- data/{jruby → lib}/neo4j/driver/ext/config_converter.rb +36 -7
- data/{jruby → lib}/neo4j/driver/ext/exception_checkable.rb +3 -3
- data/lib/neo4j/driver/ext/exception_mapper.rb +173 -0
- data/lib/neo4j/driver/ext/graph_database.rb +25 -0
- data/lib/neo4j/driver/ext/internal/clock_adapter.rb +33 -0
- data/lib/neo4j/driver/ext/internal/cluster/routing_table_registry_impl.rb +28 -0
- data/lib/neo4j/driver/ext/internal/driver_factory.rb +54 -0
- data/lib/neo4j/driver/ext/internal/internal_notification_severity.rb +15 -0
- data/lib/neo4j/driver/ext/internal/reflection.rb +22 -0
- data/lib/neo4j/driver/ext/internal/summary/internal_gql_notification.rb +22 -0
- data/lib/neo4j/driver/ext/internal/summary/internal_gql_status_object.rb +18 -0
- data/{jruby → lib}/neo4j/driver/ext/internal/summary/internal_plan.rb +3 -5
- data/lib/neo4j/driver/ext/internal/summary/internal_query_profile.rb +36 -0
- data/lib/neo4j/driver/ext/internal/summary/internal_result_summary.rb +44 -0
- data/lib/neo4j/driver/ext/internal_driver.rb +102 -0
- data/{jruby → lib}/neo4j/driver/ext/internal_result.rb +2 -2
- data/{jruby → lib}/neo4j/driver/ext/internal_session.rb +5 -1
- data/lib/neo4j/driver/ext/logger.rb +55 -0
- data/lib/neo4j/driver/ext/map_converter.rb +13 -0
- data/lib/neo4j/driver/ext/neo_converter.rb +70 -0
- data/lib/neo4j/driver/ext/plain_map_converter.rb +11 -0
- data/{jruby → lib}/neo4j/driver/ext/ruby_converter.rb +20 -6
- data/lib/neo4j/driver/ext/unsupported_type.rb +14 -0
- data/lib/neo4j/driver/internal/deprecator.rb +9 -5
- data/lib/neo4j/driver/internal/duration_normalizer.rb +14 -39
- data/lib/neo4j/driver/internal/internal_auth_token_manager.rb +37 -0
- data/lib/neo4j/driver/internal/validator.rb +16 -2
- data/lib/neo4j/driver/summary/query_type.rb +5 -1
- data/lib/neo4j/driver/types/duration.rb +76 -0
- data/lib/neo4j/driver/types/local_date_time.rb +77 -8
- data/lib/neo4j/driver/types/local_time.rb +50 -7
- data/lib/neo4j/driver/types/offset_time.rb +78 -7
- data/lib/neo4j/driver/types/point.rb +38 -18
- data/lib/neo4j/driver/types/temporal_value.rb +44 -0
- data/lib/neo4j/driver/types/unresolvable_zoned_date_time.rb +38 -0
- data/{jruby → lib}/neo4j/driver/version.rb +1 -1
- data/{jruby → lib}/neo4j/driver.rb +31 -7
- data/lib/neo4j-ruby-driver_jars.rb +14 -0
- data/lib/neo4j-ruby-driver_loader.rb +34 -20
- data/lib/org/neo4j/driver/neo4j-java-driver-all/6.2.1/neo4j-java-driver-all-6.2.1.jar +0 -0
- data/lib/org/neo4j/driver/neo4j-java-driver-observation-metrics/6.2.1/neo4j-java-driver-observation-metrics-6.2.1.jar +0 -0
- data/lib/org/reactivestreams/reactive-streams/1.0.4/reactive-streams-1.0.4.jar +0 -0
- metadata +76 -226
- data/jruby/neo4j/driver/ext/async_converter.rb +0 -55
- data/jruby/neo4j/driver/ext/exception_mapper.rb +0 -92
- data/jruby/neo4j/driver/ext/graph_database.rb +0 -33
- data/jruby/neo4j/driver/ext/internal/async/internal_async_session.rb +0 -23
- data/jruby/neo4j/driver/ext/internal/cluster/routing_table_registry_impl.rb +0 -15
- data/jruby/neo4j/driver/ext/internal/cursor/disposable_async_result_cursor.rb +0 -15
- data/jruby/neo4j/driver/ext/internal/driver_factory.rb +0 -24
- data/jruby/neo4j/driver/ext/internal/summary/internal_result_summary.rb +0 -34
- data/jruby/neo4j/driver/ext/internal_driver.rb +0 -52
- data/jruby/neo4j/driver/ext/logger.rb +0 -60
- data/jruby/neo4j/driver/ext/map_converter.rb +0 -14
- data/jruby/neo4j/driver/ext/neo_converter.rb +0 -59
- data/lib/neo4j/driver/internal/bolt_server_address.rb +0 -97
- data/lib/neo4j/driver/synchronizable.rb +0 -23
- data/lib/neo4j/driver/types/time.rb +0 -45
- /data/{jruby → lib}/neo4j/driver/ext/internal/abstract_query_runner.rb +0 -0
- /data/{jruby → lib}/neo4j/driver/ext/internal/eager_result_value.rb +0 -0
- /data/{jruby → lib}/neo4j/driver/ext/internal/metrics/internal_connection_pool_metrics.rb +0 -0
- /data/{jruby → lib}/neo4j/driver/ext/internal/summary/internal_notification.rb +0 -0
- /data/{jruby → lib}/neo4j/driver/ext/internal_entity.rb +0 -0
- /data/{jruby → lib}/neo4j/driver/ext/internal_keys.rb +0 -0
- /data/{jruby → lib}/neo4j/driver/ext/internal_node.rb +0 -0
- /data/{jruby → lib}/neo4j/driver/ext/internal_record.rb +0 -0
- /data/{jruby → lib}/neo4j/driver/ext/internal_relationship.rb +0 -0
- /data/{jruby → lib}/neo4j/driver/ext/internal_transaction.rb +0 -0
- /data/{jruby → lib}/neo4j/driver/ext/query.rb +0 -0
- /data/{jruby → lib}/neo4j/driver/ext/run_override.rb +0 -0
- /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(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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', '
|
|
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(
|
|
32
|
+
value = java.time.Duration.ofMillis(timeout_to_milliseconds(value))
|
|
28
33
|
when /time(out)?$/, 'routing_table_purge_delay'
|
|
29
|
-
value =
|
|
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) {
|
|
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
|
-
|
|
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(&
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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,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
|